@@ -254,12 +254,15 @@ static JuceAppDelegate* juceAppDelegate = 0; | |||||
void MessageManager::runDispatchLoop() | void MessageManager::runDispatchLoop() | ||||
{ | { | ||||
const ScopedAutoReleasePool pool; | |||||
if (! quitMessagePosted) // check that the quit message wasn't already posted.. | |||||
{ | |||||
const ScopedAutoReleasePool pool; | |||||
// must only be called by the message thread! | |||||
jassert (isThisTheMessageThread()); | |||||
// must only be called by the message thread! | |||||
jassert (isThisTheMessageThread()); | |||||
[NSApp run]; | |||||
[NSApp run]; | |||||
} | |||||
} | } | ||||
void MessageManager::stopDispatchLoop() | void MessageManager::stopDispatchLoop() | ||||
@@ -8636,7 +8636,7 @@ public: | |||||
bool isExhausted() throw() | bool isExhausted() throw() | ||||
{ | { | ||||
return pos >= zipEntryInfo.compressedSize; | |||||
return headerSize <= 0 || pos >= zipEntryInfo.compressedSize; | |||||
} | } | ||||
int64 getPosition() throw() | int64 getPosition() throw() | ||||
@@ -37043,7 +37043,7 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const | |||||
{ | { | ||||
if (MessageManager::instance->currentThreadHasLockedMessageManager()) | if (MessageManager::instance->currentThreadHasLockedMessageManager()) | ||||
{ | { | ||||
locked = true; // either we're on the message thread, or this it's a re-entrant call. | |||||
locked = true; // either we're on the message thread, or this is a re-entrant call. | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -37422,7 +37422,6 @@ public: | |||||
addTimer (t); | addTimer (t); | ||||
const ScopedUnlock ul (lock); | const ScopedUnlock ul (lock); | ||||
callbackNeeded = false; | |||||
JUCE_TRY | JUCE_TRY | ||||
{ | { | ||||
@@ -44946,7 +44945,8 @@ BEGIN_JUCE_NAMESPACE | |||||
ProgressBar::ProgressBar (double& progress_) | ProgressBar::ProgressBar (double& progress_) | ||||
: progress (progress_), | : progress (progress_), | ||||
displayPercentage (true) | |||||
displayPercentage (true), | |||||
lastCallbackTime (0) | |||||
{ | { | ||||
currentValue = jlimit (0.0, 1.0, progress); | currentValue = jlimit (0.0, 1.0, progress); | ||||
} | } | ||||
@@ -45014,9 +45014,14 @@ void ProgressBar::timerCallback() | |||||
{ | { | ||||
if (currentValue < newProgress | if (currentValue < newProgress | ||||
&& newProgress >= 0 && newProgress < 1.0 | && newProgress >= 0 && newProgress < 1.0 | ||||
&& currentValue >= 0 && newProgress < 1.0) | |||||
&& currentValue >= 0 && currentValue < 1.0) | |||||
{ | { | ||||
newProgress = jmin (currentValue + 0.02, newProgress); | |||||
const uint32 now = Time::getMillisecondCounter(); | |||||
const int timeSinceLastCallback = (int) (now - lastCallbackTime); | |||||
lastCallbackTime = now; | |||||
newProgress = jmin (currentValue + 0.00018 * timeSinceLastCallback, | |||||
newProgress); | |||||
} | } | ||||
currentValue = newProgress; | currentValue = newProgress; | ||||
@@ -51606,7 +51611,8 @@ END_JUCE_NAMESPACE | |||||
BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
class TreeViewContentComponent : public Component | |||||
class TreeViewContentComponent : public Component, | |||||
public TooltipClient | |||||
{ | { | ||||
public: | public: | ||||
TreeViewContentComponent (TreeView* const owner_) | TreeViewContentComponent (TreeView* const owner_) | ||||
@@ -51868,6 +51874,20 @@ public: | |||||
owner->itemsChanged(); | owner->itemsChanged(); | ||||
} | } | ||||
const String getTooltip() | |||||
{ | |||||
int x, y; | |||||
getMouseXYRelative (x, y); | |||||
Rectangle pos; | |||||
TreeViewItem* const item = findItemAt (y, pos); | |||||
if (item != 0) | |||||
return item->getTooltip(); | |||||
return owner->getTooltip(); | |||||
} | |||||
juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
private: | private: | ||||
@@ -52533,6 +52553,11 @@ void TreeViewItem::itemSelectionChanged (bool) | |||||
{ | { | ||||
} | } | ||||
const String TreeViewItem::getTooltip() | |||||
{ | |||||
return String::empty; | |||||
} | |||||
const String TreeViewItem::getDragSourceDescription() | const String TreeViewItem::getDragSourceDescription() | ||||
{ | { | ||||
return String::empty; | return String::empty; | ||||
@@ -60487,10 +60512,8 @@ AlertWindow* LookAndFeel::createAlertWindow (const String& title, | |||||
aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); | aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); | ||||
aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); | aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); | ||||
} | } | ||||
else | |||||
else if (numButtons == 3) | |||||
{ | { | ||||
jassert (numButtons == 3); | |||||
aw->addButton (button1, 1, button1ShortCut); | aw->addButton (button1, 1, button1ShortCut); | ||||
aw->addButton (button2, 2, button2ShortCut); | aw->addButton (button2, 2, button2ShortCut); | ||||
aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); | aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); | ||||
@@ -61571,7 +61594,7 @@ ImageEffectFilter* LookAndFeel::getSliderEffect() | |||||
static const TextLayout layoutTooltipText (const String& text) throw() | static const TextLayout layoutTooltipText (const String& text) throw() | ||||
{ | { | ||||
const float tooltipFontSize = 15.0f; | |||||
const float tooltipFontSize = 12.0f; | |||||
const int maxToolTipWidth = 400; | const int maxToolTipWidth = 400; | ||||
const Font f (tooltipFontSize, Font::bold); | const Font f (tooltipFontSize, Font::bold); | ||||
@@ -61586,7 +61609,7 @@ void LookAndFeel::getTooltipSize (const String& tipText, int& width, int& height | |||||
const TextLayout tl (layoutTooltipText (tipText)); | const TextLayout tl (layoutTooltipText (tipText)); | ||||
width = tl.getWidth() + 14; | width = tl.getWidth() + 14; | ||||
height = tl.getHeight() + 10; | |||||
height = tl.getHeight() + 6; | |||||
} | } | ||||
void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height) | void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height) | ||||
@@ -61595,8 +61618,10 @@ void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int h | |||||
const Colour textCol (findColour (TooltipWindow::textColourId)); | const Colour textCol (findColour (TooltipWindow::textColourId)); | ||||
#if ! JUCE_MAC // The mac windows already have a non-optional 1 pix outline, so don't double it here.. | |||||
g.setColour (findColour (TooltipWindow::outlineColourId)); | g.setColour (findColour (TooltipWindow::outlineColourId)); | ||||
g.drawRect (0, 0, width, height); | |||||
g.drawRect (0, 0, width, height, 1); | |||||
#endif | |||||
const TextLayout tl (layoutTooltipText (text)); | const TextLayout tl (layoutTooltipText (text)); | ||||
@@ -72476,10 +72501,9 @@ bool ComponentPeer::handleKeyPress (const int keyCode, | |||||
if (keyInfo.isKeyCode (KeyPress::tabKey) && Component::getCurrentlyFocusedComponent() != 0) | if (keyInfo.isKeyCode (KeyPress::tabKey) && Component::getCurrentlyFocusedComponent() != 0) | ||||
{ | { | ||||
Component::getCurrentlyFocusedComponent() | |||||
->moveKeyboardFocusToSibling (! keyInfo.getModifiers().isShiftDown()); | |||||
keyWasUsed = true; | |||||
Component* const currentlyFocused = Component::getCurrentlyFocusedComponent(); | |||||
currentlyFocused->moveKeyboardFocusToSibling (! keyInfo.getModifiers().isShiftDown()); | |||||
keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent()); | |||||
break; | break; | ||||
} | } | ||||
@@ -73869,14 +73893,12 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, | |||||
timeOutMsWhenCancelling (timeOutMsWhenCancelling_) | timeOutMsWhenCancelling (timeOutMsWhenCancelling_) | ||||
{ | { | ||||
alertWindow = LookAndFeel::getDefaultLookAndFeel() | alertWindow = LookAndFeel::getDefaultLookAndFeel() | ||||
.createAlertWindow (title, String::empty, cancelButtonText, String::empty, String::empty, | |||||
AlertWindow::NoIcon, 1, 0); | |||||
.createAlertWindow (title, String::empty, cancelButtonText, | |||||
String::empty, String::empty, | |||||
AlertWindow::NoIcon, hasCancelButton ? 1 : 0, 0); | |||||
if (hasProgressBar) | if (hasProgressBar) | ||||
alertWindow->addProgressBarComponent (progress); | alertWindow->addProgressBarComponent (progress); | ||||
if (hasCancelButton) | |||||
alertWindow->addButton (cancelButtonText, 1); | |||||
} | } | ||||
ThreadWithProgressWindow::~ThreadWithProgressWindow() | ThreadWithProgressWindow::~ThreadWithProgressWindow() | ||||
@@ -73895,13 +73917,13 @@ bool ThreadWithProgressWindow::runThread (const int priority) | |||||
alertWindow->setMessage (message); | alertWindow->setMessage (message); | ||||
} | } | ||||
const bool wasCancelled = alertWindow->runModalLoop() != 0; | |||||
const bool finishedNaturally = alertWindow->runModalLoop() != 0; | |||||
stopThread (timeOutMsWhenCancelling); | stopThread (timeOutMsWhenCancelling); | ||||
alertWindow->setVisible (false); | alertWindow->setVisible (false); | ||||
return ! wasCancelled; | |||||
return finishedNaturally; | |||||
} | } | ||||
void ThreadWithProgressWindow::setProgress (const double newProgress) | void ThreadWithProgressWindow::setProgress (const double newProgress) | ||||
@@ -73920,7 +73942,7 @@ void ThreadWithProgressWindow::timerCallback() | |||||
if (! isThreadRunning()) | if (! isThreadRunning()) | ||||
{ | { | ||||
// thread has finished normally.. | // thread has finished normally.. | ||||
alertWindow->exitModalState (0); | |||||
alertWindow->exitModalState (1); | |||||
alertWindow->setVisible (false); | alertWindow->setVisible (false); | ||||
} | } | ||||
else | else | ||||
@@ -73943,7 +73965,6 @@ TooltipWindow::TooltipWindow (Component* const parentComponent, | |||||
millisecondsBeforeTipAppears (millisecondsBeforeTipAppears_), | millisecondsBeforeTipAppears (millisecondsBeforeTipAppears_), | ||||
mouseX (0), | mouseX (0), | ||||
mouseY (0), | mouseY (0), | ||||
lastMouseMoveTime (0), | |||||
lastHideTime (0), | lastHideTime (0), | ||||
lastComponentUnderMouse (0), | lastComponentUnderMouse (0), | ||||
changedCompsSinceShown (true) | changedCompsSinceShown (true) | ||||
@@ -73964,7 +73985,7 @@ TooltipWindow::~TooltipWindow() | |||||
void TooltipWindow::paint (Graphics& g) | void TooltipWindow::paint (Graphics& g) | ||||
{ | { | ||||
getLookAndFeel().drawTooltip (g, tip, getWidth(), getHeight()); | |||||
getLookAndFeel().drawTooltip (g, tipShowing, getWidth(), getHeight()); | |||||
} | } | ||||
void TooltipWindow::mouseEnter (const MouseEvent&) | void TooltipWindow::mouseEnter (const MouseEvent&) | ||||
@@ -73972,104 +73993,113 @@ void TooltipWindow::mouseEnter (const MouseEvent&) | |||||
hide(); | hide(); | ||||
} | } | ||||
void TooltipWindow::showFor (Component* const c) | |||||
void TooltipWindow::showFor (Component* const c, const String& tip) | |||||
{ | { | ||||
TooltipClient* const ttc = dynamic_cast <TooltipClient*> (c); | |||||
jassert (tip.isNotEmpty()); | |||||
tipShowing = tip; | |||||
if (ttc != 0 && ! c->isCurrentlyBlockedByAnotherModalComponent()) | |||||
tip = ttc->getTooltip(); | |||||
else | |||||
tip = String::empty; | |||||
int mx, my; | |||||
Desktop::getMousePosition (mx, my); | |||||
if (tip.isEmpty()) | |||||
{ | |||||
hide(); | |||||
} | |||||
else | |||||
{ | |||||
int mx, my; | |||||
Desktop::getMousePosition (mx, my); | |||||
if (getParentComponent() != 0) | |||||
getParentComponent()->globalPositionToRelative (mx, my); | |||||
if (getParentComponent() != 0) | |||||
getParentComponent()->globalPositionToRelative (mx, my); | |||||
int x, y, w, h; | |||||
getLookAndFeel().getTooltipSize (tip, w, h); | |||||
int x, y, w, h; | |||||
getLookAndFeel().getTooltipSize (tip, w, h); | |||||
if (mx > getParentWidth() / 2) | |||||
x = mx - (w + 12); | |||||
else | |||||
x = mx + 24; | |||||
if (mx > getParentWidth() / 2) | |||||
x = mx - (w + 12); | |||||
else | |||||
x = mx + 24; | |||||
if (my > getParentHeight() / 2) | |||||
y = my - (h + 6); | |||||
else | |||||
y = my + 6; | |||||
if (my > getParentHeight() / 2) | |||||
y = my - (h + 6); | |||||
else | |||||
y = my + 6; | |||||
setBounds (x, y, w, h); | |||||
setVisible (true); | |||||
setBounds (x, y, w, h); | |||||
setVisible (true); | |||||
if (getParentComponent() == 0) | |||||
{ | |||||
addToDesktop (ComponentPeer::windowHasDropShadow | |||||
| ComponentPeer::windowIsTemporary); | |||||
} | |||||
if (getParentComponent() == 0) | |||||
{ | |||||
addToDesktop (ComponentPeer::windowHasDropShadow | |||||
| ComponentPeer::windowIsTemporary); | |||||
} | |||||
toFront (false); | |||||
} | |||||
toFront (false); | |||||
const String TooltipWindow::getTipFor (Component* const c) | |||||
{ | |||||
if (c->isValidComponent() && Process::isForegroundProcess()) | |||||
{ | |||||
TooltipClient* const ttc = dynamic_cast <TooltipClient*> (c); | |||||
if (ttc != 0 && ! c->isCurrentlyBlockedByAnotherModalComponent()) | |||||
return ttc->getTooltip(); | |||||
} | } | ||||
return String::empty; | |||||
} | } | ||||
void TooltipWindow::hide() | void TooltipWindow::hide() | ||||
{ | { | ||||
tipShowing = String::empty; | |||||
removeFromDesktop(); | removeFromDesktop(); | ||||
setVisible (false); | setVisible (false); | ||||
} | } | ||||
void TooltipWindow::timerCallback() | void TooltipWindow::timerCallback() | ||||
{ | { | ||||
const unsigned int now = Time::getApproximateMillisecondCounter(); | |||||
Component* const newComp = Component::getComponentUnderMouse(); | |||||
const String newTip (getTipFor (newComp)); | |||||
const bool tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse); | |||||
lastComponentUnderMouse = newComp; | |||||
lastTipUnderMouse = newTip; | |||||
const int clickCount = Desktop::getInstance().getMouseButtonClickCounter(); | |||||
const bool mouseWasClicked = clickCount > mouseClicks; | |||||
mouseClicks = clickCount; | |||||
int mx, my; | int mx, my; | ||||
Desktop::getMousePosition (mx, my); | Desktop::getMousePosition (mx, my); | ||||
const bool mouseMovedQuickly = (abs (mx - mouseX) + abs (my - mouseY) > 12); | |||||
mouseX = mx; | |||||
mouseY = my; | |||||
const unsigned int now = Time::getApproximateMillisecondCounter(); | |||||
Component* const underMouse = Component::getComponentUnderMouse(); | |||||
const bool changedComp = (underMouse != lastComponentUnderMouse); | |||||
lastComponentUnderMouse = underMouse; | |||||
if (tipChanged || mouseWasClicked || mouseMovedQuickly) | |||||
lastCompChangeTime = now; | |||||
if (changedComp | |||||
|| abs (mx - mouseX) > 4 | |||||
|| abs (my - mouseY) > 4 | |||||
|| Desktop::getInstance().getMouseButtonClickCounter() > mouseClicks) | |||||
if (isVisible() || now < lastHideTime + 500) | |||||
{ | { | ||||
lastMouseMoveTime = now; | |||||
if (isVisible()) | |||||
// if a tip is currently visible (or has just disappeared), update to a new one | |||||
// immediately if needed.. | |||||
if (newComp == 0 || mouseWasClicked || newTip.isEmpty()) | |||||
{ | { | ||||
lastHideTime = now; | |||||
hide(); | |||||
if (isVisible()) | |||||
{ | |||||
lastHideTime = now; | |||||
hide(); | |||||
} | |||||
} | |||||
else if (tipChanged) | |||||
{ | |||||
showFor (newComp, newTip); | |||||
} | } | ||||
changedCompsSinceShown = changedCompsSinceShown || changedComp; | |||||
tip = String::empty; | |||||
mouseX = mx; | |||||
mouseY = my; | |||||
} | } | ||||
if (changedCompsSinceShown) | |||||
else | |||||
{ | { | ||||
if ((now > lastMouseMoveTime + millisecondsBeforeTipAppears | |||||
|| now < lastHideTime + 500) | |||||
&& ! isVisible()) | |||||
// if there isn't currently a tip, but one is needed, only let it | |||||
// appear after a timeout.. | |||||
if (newTip.isNotEmpty() | |||||
&& newTip != tipShowing | |||||
&& now > lastCompChangeTime + millisecondsBeforeTipAppears) | |||||
{ | { | ||||
if (underMouse->isValidComponent()) | |||||
showFor (underMouse); | |||||
changedCompsSinceShown = false; | |||||
showFor (newComp, newTip); | |||||
} | } | ||||
} | } | ||||
mouseClicks = Desktop::getInstance().getMouseButtonClickCounter(); | |||||
} | } | ||||
END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
@@ -270323,12 +270353,15 @@ static JuceAppDelegate* juceAppDelegate = 0; | |||||
void MessageManager::runDispatchLoop() | void MessageManager::runDispatchLoop() | ||||
{ | { | ||||
const ScopedAutoReleasePool pool; | |||||
if (! quitMessagePosted) // check that the quit message wasn't already posted.. | |||||
{ | |||||
const ScopedAutoReleasePool pool; | |||||
// must only be called by the message thread! | |||||
jassert (isThisTheMessageThread()); | |||||
// must only be called by the message thread! | |||||
jassert (isThisTheMessageThread()); | |||||
[NSApp run]; | |||||
[NSApp run]; | |||||
} | |||||
} | } | ||||
void MessageManager::stopDispatchLoop() | void MessageManager::stopDispatchLoop() | ||||
@@ -29942,16 +29942,17 @@ private: | |||||
const int millisecondsBeforeTipAppears; | const int millisecondsBeforeTipAppears; | ||||
int mouseX, mouseY, mouseClicks; | int mouseX, mouseY, mouseClicks; | ||||
unsigned int lastMouseMoveTime, lastHideTime; | |||||
unsigned int lastCompChangeTime, lastHideTime; | |||||
Component* lastComponentUnderMouse; | Component* lastComponentUnderMouse; | ||||
bool changedCompsSinceShown; | bool changedCompsSinceShown; | ||||
String tip; | |||||
String tipShowing, lastTipUnderMouse; | |||||
void paint (Graphics& g); | void paint (Graphics& g); | ||||
void mouseEnter (const MouseEvent& e); | void mouseEnter (const MouseEvent& e); | ||||
void timerCallback(); | void timerCallback(); | ||||
void showFor (Component* const c); | |||||
static const String getTipFor (Component* const c); | |||||
void showFor (Component* const c, const String& tip); | |||||
void hide(); | void hide(); | ||||
TooltipWindow (const TooltipWindow&); | TooltipWindow (const TooltipWindow&); | ||||
@@ -43355,6 +43356,11 @@ public: | |||||
*/ | */ | ||||
virtual void itemSelectionChanged (bool isNowSelected); | virtual void itemSelectionChanged (bool isNowSelected); | ||||
/** The item can return a tool tip string here if it wants to. | |||||
@see TooltipClient | |||||
*/ | |||||
virtual const String getTooltip(); | |||||
/** To allow items from your treeview to be dragged-and-dropped, implement this method. | /** To allow items from your treeview to be dragged-and-dropped, implement this method. | ||||
If this returns a non-empty name then when the user drags an item, the treeview will | If this returns a non-empty name then when the user drags an item, the treeview will | ||||
@@ -45947,6 +45953,7 @@ private: | |||||
double currentValue; | double currentValue; | ||||
bool displayPercentage; | bool displayPercentage; | ||||
String displayedMessage, currentMessage; | String displayedMessage, currentMessage; | ||||
uint32 lastCallbackTime; | |||||
void timerCallback(); | void timerCallback(); | ||||
@@ -278,7 +278,7 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const | |||||
{ | { | ||||
if (MessageManager::instance->currentThreadHasLockedMessageManager()) | if (MessageManager::instance->currentThreadHasLockedMessageManager()) | ||||
{ | { | ||||
locked = true; // either we're on the message thread, or this it's a re-entrant call. | |||||
locked = true; // either we're on the message thread, or this is a re-entrant call. | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -247,7 +247,6 @@ public: | |||||
addTimer (t); | addTimer (t); | ||||
const ScopedUnlock ul (lock); | const ScopedUnlock ul (lock); | ||||
callbackNeeded = false; | |||||
JUCE_TRY | JUCE_TRY | ||||
{ | { | ||||
@@ -40,7 +40,8 @@ BEGIN_JUCE_NAMESPACE | |||||
//============================================================================== | //============================================================================== | ||||
ProgressBar::ProgressBar (double& progress_) | ProgressBar::ProgressBar (double& progress_) | ||||
: progress (progress_), | : progress (progress_), | ||||
displayPercentage (true) | |||||
displayPercentage (true), | |||||
lastCallbackTime (0) | |||||
{ | { | ||||
currentValue = jlimit (0.0, 1.0, progress); | currentValue = jlimit (0.0, 1.0, progress); | ||||
} | } | ||||
@@ -109,9 +110,14 @@ void ProgressBar::timerCallback() | |||||
{ | { | ||||
if (currentValue < newProgress | if (currentValue < newProgress | ||||
&& newProgress >= 0 && newProgress < 1.0 | && newProgress >= 0 && newProgress < 1.0 | ||||
&& currentValue >= 0 && newProgress < 1.0) | |||||
&& currentValue >= 0 && currentValue < 1.0) | |||||
{ | { | ||||
newProgress = jmin (currentValue + 0.02, newProgress); | |||||
const uint32 now = Time::getMillisecondCounter(); | |||||
const int timeSinceLastCallback = (int) (now - lastCallbackTime); | |||||
lastCallbackTime = now; | |||||
newProgress = jmin (currentValue + 0.00018 * timeSinceLastCallback, | |||||
newProgress); | |||||
} | } | ||||
currentValue = newProgress; | currentValue = newProgress; | ||||
@@ -119,6 +119,7 @@ private: | |||||
double currentValue; | double currentValue; | ||||
bool displayPercentage; | bool displayPercentage; | ||||
String displayedMessage, currentMessage; | String displayedMessage, currentMessage; | ||||
uint32 lastCallbackTime; | |||||
void timerCallback(); | void timerCallback(); | ||||
@@ -41,7 +41,8 @@ BEGIN_JUCE_NAMESPACE | |||||
//============================================================================== | //============================================================================== | ||||
class TreeViewContentComponent : public Component | |||||
class TreeViewContentComponent : public Component, | |||||
public TooltipClient | |||||
{ | { | ||||
public: | public: | ||||
TreeViewContentComponent (TreeView* const owner_) | TreeViewContentComponent (TreeView* const owner_) | ||||
@@ -303,6 +304,20 @@ public: | |||||
owner->itemsChanged(); | owner->itemsChanged(); | ||||
} | } | ||||
const String getTooltip() | |||||
{ | |||||
int x, y; | |||||
getMouseXYRelative (x, y); | |||||
Rectangle pos; | |||||
TreeViewItem* const item = findItemAt (y, pos); | |||||
if (item != 0) | |||||
return item->getTooltip(); | |||||
return owner->getTooltip(); | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
@@ -980,6 +995,11 @@ void TreeViewItem::itemSelectionChanged (bool) | |||||
{ | { | ||||
} | } | ||||
const String TreeViewItem::getTooltip() | |||||
{ | |||||
return String::empty; | |||||
} | |||||
const String TreeViewItem::getDragSourceDescription() | const String TreeViewItem::getDragSourceDescription() | ||||
{ | { | ||||
return String::empty; | return String::empty; | ||||
@@ -337,6 +337,11 @@ public: | |||||
*/ | */ | ||||
virtual void itemSelectionChanged (bool isNowSelected); | virtual void itemSelectionChanged (bool isNowSelected); | ||||
/** The item can return a tool tip string here if it wants to. | |||||
@see TooltipClient | |||||
*/ | |||||
virtual const String getTooltip(); | |||||
/** To allow items from your treeview to be dragged-and-dropped, implement this method. | /** To allow items from your treeview to be dragged-and-dropped, implement this method. | ||||
If this returns a non-empty name then when the user drags an item, the treeview will | If this returns a non-empty name then when the user drags an item, the treeview will | ||||
@@ -495,10 +495,8 @@ AlertWindow* LookAndFeel::createAlertWindow (const String& title, | |||||
aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); | aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); | ||||
aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); | aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); | ||||
} | } | ||||
else | |||||
else if (numButtons == 3) | |||||
{ | { | ||||
jassert (numButtons == 3); | |||||
aw->addButton (button1, 1, button1ShortCut); | aw->addButton (button1, 1, button1ShortCut); | ||||
aw->addButton (button2, 2, button2ShortCut); | aw->addButton (button2, 2, button2ShortCut); | ||||
aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); | aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); | ||||
@@ -1591,7 +1589,7 @@ ImageEffectFilter* LookAndFeel::getSliderEffect() | |||||
//============================================================================== | //============================================================================== | ||||
static const TextLayout layoutTooltipText (const String& text) throw() | static const TextLayout layoutTooltipText (const String& text) throw() | ||||
{ | { | ||||
const float tooltipFontSize = 15.0f; | |||||
const float tooltipFontSize = 12.0f; | |||||
const int maxToolTipWidth = 400; | const int maxToolTipWidth = 400; | ||||
const Font f (tooltipFontSize, Font::bold); | const Font f (tooltipFontSize, Font::bold); | ||||
@@ -1606,7 +1604,7 @@ void LookAndFeel::getTooltipSize (const String& tipText, int& width, int& height | |||||
const TextLayout tl (layoutTooltipText (tipText)); | const TextLayout tl (layoutTooltipText (tipText)); | ||||
width = tl.getWidth() + 14; | width = tl.getWidth() + 14; | ||||
height = tl.getHeight() + 10; | |||||
height = tl.getHeight() + 6; | |||||
} | } | ||||
void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height) | void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height) | ||||
@@ -1615,8 +1613,10 @@ void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int h | |||||
const Colour textCol (findColour (TooltipWindow::textColourId)); | const Colour textCol (findColour (TooltipWindow::textColourId)); | ||||
#if ! JUCE_MAC // The mac windows already have a non-optional 1 pix outline, so don't double it here.. | |||||
g.setColour (findColour (TooltipWindow::outlineColourId)); | g.setColour (findColour (TooltipWindow::outlineColourId)); | ||||
g.drawRect (0, 0, width, height); | |||||
g.drawRect (0, 0, width, height, 1); | |||||
#endif | |||||
const TextLayout tl (layoutTooltipText (text)); | const TextLayout tl (layoutTooltipText (text)); | ||||
@@ -461,13 +461,12 @@ bool ComponentPeer::handleKeyPress (const int keyCode, | |||||
if (keyInfo.isKeyCode (KeyPress::tabKey) && Component::getCurrentlyFocusedComponent() != 0) | if (keyInfo.isKeyCode (KeyPress::tabKey) && Component::getCurrentlyFocusedComponent() != 0) | ||||
{ | { | ||||
Component::getCurrentlyFocusedComponent() | |||||
->moveKeyboardFocusToSibling (! keyInfo.getModifiers().isShiftDown()); | |||||
keyWasUsed = true; | |||||
Component* const currentlyFocused = Component::getCurrentlyFocusedComponent(); | |||||
currentlyFocused->moveKeyboardFocusToSibling (! keyInfo.getModifiers().isShiftDown()); | |||||
keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent()); | |||||
break; | break; | ||||
} | } | ||||
target = target->parentComponent_; | target = target->parentComponent_; | ||||
} | } | ||||
@@ -50,14 +50,12 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, | |||||
timeOutMsWhenCancelling (timeOutMsWhenCancelling_) | timeOutMsWhenCancelling (timeOutMsWhenCancelling_) | ||||
{ | { | ||||
alertWindow = LookAndFeel::getDefaultLookAndFeel() | alertWindow = LookAndFeel::getDefaultLookAndFeel() | ||||
.createAlertWindow (title, String::empty, cancelButtonText, String::empty, String::empty, | |||||
AlertWindow::NoIcon, 1, 0); | |||||
.createAlertWindow (title, String::empty, cancelButtonText, | |||||
String::empty, String::empty, | |||||
AlertWindow::NoIcon, hasCancelButton ? 1 : 0, 0); | |||||
if (hasProgressBar) | if (hasProgressBar) | ||||
alertWindow->addProgressBarComponent (progress); | alertWindow->addProgressBarComponent (progress); | ||||
if (hasCancelButton) | |||||
alertWindow->addButton (cancelButtonText, 1); | |||||
} | } | ||||
ThreadWithProgressWindow::~ThreadWithProgressWindow() | ThreadWithProgressWindow::~ThreadWithProgressWindow() | ||||
@@ -76,13 +74,13 @@ bool ThreadWithProgressWindow::runThread (const int priority) | |||||
alertWindow->setMessage (message); | alertWindow->setMessage (message); | ||||
} | } | ||||
const bool wasCancelled = alertWindow->runModalLoop() != 0; | |||||
const bool finishedNaturally = alertWindow->runModalLoop() != 0; | |||||
stopThread (timeOutMsWhenCancelling); | stopThread (timeOutMsWhenCancelling); | ||||
alertWindow->setVisible (false); | alertWindow->setVisible (false); | ||||
return ! wasCancelled; | |||||
return finishedNaturally; | |||||
} | } | ||||
void ThreadWithProgressWindow::setProgress (const double newProgress) | void ThreadWithProgressWindow::setProgress (const double newProgress) | ||||
@@ -101,7 +99,7 @@ void ThreadWithProgressWindow::timerCallback() | |||||
if (! isThreadRunning()) | if (! isThreadRunning()) | ||||
{ | { | ||||
// thread has finished normally.. | // thread has finished normally.. | ||||
alertWindow->exitModalState (0); | |||||
alertWindow->exitModalState (1); | |||||
alertWindow->setVisible (false); | alertWindow->setVisible (false); | ||||
} | } | ||||
else | else | ||||
@@ -36,6 +36,7 @@ BEGIN_JUCE_NAMESPACE | |||||
#include "juce_TooltipWindow.h" | #include "juce_TooltipWindow.h" | ||||
#include "../../../../juce_core/basics/juce_Time.h" | #include "../../../../juce_core/basics/juce_Time.h" | ||||
#include "../../../../juce_core/threads/juce_Process.h" | |||||
#include "../lookandfeel/juce_LookAndFeel.h" | #include "../lookandfeel/juce_LookAndFeel.h" | ||||
#include "../juce_Desktop.h" | #include "../juce_Desktop.h" | ||||
@@ -47,7 +48,6 @@ TooltipWindow::TooltipWindow (Component* const parentComponent, | |||||
millisecondsBeforeTipAppears (millisecondsBeforeTipAppears_), | millisecondsBeforeTipAppears (millisecondsBeforeTipAppears_), | ||||
mouseX (0), | mouseX (0), | ||||
mouseY (0), | mouseY (0), | ||||
lastMouseMoveTime (0), | |||||
lastHideTime (0), | lastHideTime (0), | ||||
lastComponentUnderMouse (0), | lastComponentUnderMouse (0), | ||||
changedCompsSinceShown (true) | changedCompsSinceShown (true) | ||||
@@ -68,7 +68,7 @@ TooltipWindow::~TooltipWindow() | |||||
void TooltipWindow::paint (Graphics& g) | void TooltipWindow::paint (Graphics& g) | ||||
{ | { | ||||
getLookAndFeel().drawTooltip (g, tip, getWidth(), getHeight()); | |||||
getLookAndFeel().drawTooltip (g, tipShowing, getWidth(), getHeight()); | |||||
} | } | ||||
void TooltipWindow::mouseEnter (const MouseEvent&) | void TooltipWindow::mouseEnter (const MouseEvent&) | ||||
@@ -76,104 +76,113 @@ void TooltipWindow::mouseEnter (const MouseEvent&) | |||||
hide(); | hide(); | ||||
} | } | ||||
void TooltipWindow::showFor (Component* const c) | |||||
void TooltipWindow::showFor (Component* const c, const String& tip) | |||||
{ | { | ||||
TooltipClient* const ttc = dynamic_cast <TooltipClient*> (c); | |||||
jassert (tip.isNotEmpty()); | |||||
tipShowing = tip; | |||||
int mx, my; | |||||
Desktop::getMousePosition (mx, my); | |||||
if (ttc != 0 && ! c->isCurrentlyBlockedByAnotherModalComponent()) | |||||
tip = ttc->getTooltip(); | |||||
else | |||||
tip = String::empty; | |||||
if (getParentComponent() != 0) | |||||
getParentComponent()->globalPositionToRelative (mx, my); | |||||
if (tip.isEmpty()) | |||||
{ | |||||
hide(); | |||||
} | |||||
else | |||||
{ | |||||
int mx, my; | |||||
Desktop::getMousePosition (mx, my); | |||||
int x, y, w, h; | |||||
getLookAndFeel().getTooltipSize (tip, w, h); | |||||
if (getParentComponent() != 0) | |||||
getParentComponent()->globalPositionToRelative (mx, my); | |||||
if (mx > getParentWidth() / 2) | |||||
x = mx - (w + 12); | |||||
else | |||||
x = mx + 24; | |||||
int x, y, w, h; | |||||
getLookAndFeel().getTooltipSize (tip, w, h); | |||||
if (my > getParentHeight() / 2) | |||||
y = my - (h + 6); | |||||
else | |||||
y = my + 6; | |||||
if (mx > getParentWidth() / 2) | |||||
x = mx - (w + 12); | |||||
else | |||||
x = mx + 24; | |||||
setBounds (x, y, w, h); | |||||
setVisible (true); | |||||
if (my > getParentHeight() / 2) | |||||
y = my - (h + 6); | |||||
else | |||||
y = my + 6; | |||||
if (getParentComponent() == 0) | |||||
{ | |||||
addToDesktop (ComponentPeer::windowHasDropShadow | |||||
| ComponentPeer::windowIsTemporary); | |||||
} | |||||
setBounds (x, y, w, h); | |||||
setVisible (true); | |||||
toFront (false); | |||||
} | |||||
if (getParentComponent() == 0) | |||||
{ | |||||
addToDesktop (ComponentPeer::windowHasDropShadow | |||||
| ComponentPeer::windowIsTemporary); | |||||
} | |||||
const String TooltipWindow::getTipFor (Component* const c) | |||||
{ | |||||
if (c->isValidComponent() && Process::isForegroundProcess()) | |||||
{ | |||||
TooltipClient* const ttc = dynamic_cast <TooltipClient*> (c); | |||||
toFront (false); | |||||
if (ttc != 0 && ! c->isCurrentlyBlockedByAnotherModalComponent()) | |||||
return ttc->getTooltip(); | |||||
} | } | ||||
return String::empty; | |||||
} | } | ||||
void TooltipWindow::hide() | void TooltipWindow::hide() | ||||
{ | { | ||||
tipShowing = String::empty; | |||||
removeFromDesktop(); | removeFromDesktop(); | ||||
setVisible (false); | setVisible (false); | ||||
} | } | ||||
void TooltipWindow::timerCallback() | void TooltipWindow::timerCallback() | ||||
{ | { | ||||
const unsigned int now = Time::getApproximateMillisecondCounter(); | |||||
Component* const newComp = Component::getComponentUnderMouse(); | |||||
const String newTip (getTipFor (newComp)); | |||||
const bool tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse); | |||||
lastComponentUnderMouse = newComp; | |||||
lastTipUnderMouse = newTip; | |||||
const int clickCount = Desktop::getInstance().getMouseButtonClickCounter(); | |||||
const bool mouseWasClicked = clickCount > mouseClicks; | |||||
mouseClicks = clickCount; | |||||
int mx, my; | int mx, my; | ||||
Desktop::getMousePosition (mx, my); | Desktop::getMousePosition (mx, my); | ||||
const bool mouseMovedQuickly = (abs (mx - mouseX) + abs (my - mouseY) > 12); | |||||
mouseX = mx; | |||||
mouseY = my; | |||||
const unsigned int now = Time::getApproximateMillisecondCounter(); | |||||
Component* const underMouse = Component::getComponentUnderMouse(); | |||||
const bool changedComp = (underMouse != lastComponentUnderMouse); | |||||
lastComponentUnderMouse = underMouse; | |||||
if (changedComp | |||||
|| abs (mx - mouseX) > 4 | |||||
|| abs (my - mouseY) > 4 | |||||
|| Desktop::getInstance().getMouseButtonClickCounter() > mouseClicks) | |||||
{ | |||||
lastMouseMoveTime = now; | |||||
if (tipChanged || mouseWasClicked || mouseMovedQuickly) | |||||
lastCompChangeTime = now; | |||||
if (isVisible()) | |||||
if (isVisible() || now < lastHideTime + 500) | |||||
{ | |||||
// if a tip is currently visible (or has just disappeared), update to a new one | |||||
// immediately if needed.. | |||||
if (newComp == 0 || mouseWasClicked || newTip.isEmpty()) | |||||
{ | { | ||||
lastHideTime = now; | |||||
hide(); | |||||
if (isVisible()) | |||||
{ | |||||
lastHideTime = now; | |||||
hide(); | |||||
} | |||||
} | |||||
else if (tipChanged) | |||||
{ | |||||
showFor (newComp, newTip); | |||||
} | } | ||||
changedCompsSinceShown = changedCompsSinceShown || changedComp; | |||||
tip = String::empty; | |||||
mouseX = mx; | |||||
mouseY = my; | |||||
} | } | ||||
if (changedCompsSinceShown) | |||||
else | |||||
{ | { | ||||
if ((now > lastMouseMoveTime + millisecondsBeforeTipAppears | |||||
|| now < lastHideTime + 500) | |||||
&& ! isVisible()) | |||||
// if there isn't currently a tip, but one is needed, only let it | |||||
// appear after a timeout.. | |||||
if (newTip.isNotEmpty() | |||||
&& newTip != tipShowing | |||||
&& now > lastCompChangeTime + millisecondsBeforeTipAppears) | |||||
{ | { | ||||
if (underMouse->isValidComponent()) | |||||
showFor (underMouse); | |||||
changedCompsSinceShown = false; | |||||
showFor (newComp, newTip); | |||||
} | } | ||||
} | } | ||||
mouseClicks = Desktop::getInstance().getMouseButtonClickCounter(); | |||||
} | } | ||||
END_JUCE_NAMESPACE | END_JUCE_NAMESPACE |
@@ -101,16 +101,17 @@ private: | |||||
//============================================================================== | //============================================================================== | ||||
const int millisecondsBeforeTipAppears; | const int millisecondsBeforeTipAppears; | ||||
int mouseX, mouseY, mouseClicks; | int mouseX, mouseY, mouseClicks; | ||||
unsigned int lastMouseMoveTime, lastHideTime; | |||||
unsigned int lastCompChangeTime, lastHideTime; | |||||
Component* lastComponentUnderMouse; | Component* lastComponentUnderMouse; | ||||
bool changedCompsSinceShown; | bool changedCompsSinceShown; | ||||
String tip; | |||||
String tipShowing, lastTipUnderMouse; | |||||
void paint (Graphics& g); | void paint (Graphics& g); | ||||
void mouseEnter (const MouseEvent& e); | void mouseEnter (const MouseEvent& e); | ||||
void timerCallback(); | void timerCallback(); | ||||
void showFor (Component* const c); | |||||
static const String getTipFor (Component* const c); | |||||
void showFor (Component* const c, const String& tip); | |||||
void hide(); | void hide(); | ||||
TooltipWindow (const TooltipWindow&); | TooltipWindow (const TooltipWindow&); | ||||
@@ -137,7 +137,7 @@ public: | |||||
bool isExhausted() throw() | bool isExhausted() throw() | ||||
{ | { | ||||
return pos >= zipEntryInfo.compressedSize; | |||||
return headerSize <= 0 || pos >= zipEntryInfo.compressedSize; | |||||
} | } | ||||
int64 getPosition() throw() | int64 getPosition() throw() | ||||