From 645637ab0920fe524ceaf15952d50abcb362da1e Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Fri, 26 Nov 2010 12:57:24 +0000 Subject: [PATCH] Added some assertions, fixed a win32 mouse mouse cursor problem, tweaked PopupMenu::showAt(). --- .../Source/demos/RenderingTestComponent.cpp | 3 +- extras/juce demo/Source/demos/WidgetsDemo.cpp | 2 +- src/events/juce_AsyncUpdater.cpp | 2 +- src/events/juce_MessageManager.cpp | 5 +- src/gui/components/juce_Component.cpp | 14 +++ src/gui/components/juce_Component.h | 20 ++++ .../menus/juce_MenuBarComponent.cpp | 4 +- src/gui/components/menus/juce_PopupMenu.cpp | 28 ++--- src/gui/components/menus/juce_PopupMenu.h | 22 ++-- .../windows/juce_ResizableWindow.cpp | 6 + .../geometry/juce_AffineTransform.cpp | 110 +++++++----------- .../graphics/geometry/juce_AffineTransform.h | 12 +- src/native/windows/juce_win32_Windowing.cpp | 9 +- src/text/juce_XmlDocument.cpp | 17 +++ 14 files changed, 135 insertions(+), 119 deletions(-) diff --git a/extras/juce demo/Source/demos/RenderingTestComponent.cpp b/extras/juce demo/Source/demos/RenderingTestComponent.cpp index c51ddb23e9..c151d65708 100644 --- a/extras/juce demo/Source/demos/RenderingTestComponent.cpp +++ b/extras/juce demo/Source/demos/RenderingTestComponent.cpp @@ -337,8 +337,7 @@ private: if (svgDrawable != 0) { - // to make our icon the right size, we'll put it inside a DrawableComposite, and - // set its bounding box to the size and position that we want. + // to make our icon the right size, we'll set its bounding box to the size and position that we want. svgDrawable->setBoundingBox (RelativeParallelogram (Point (-100, -100), Point (100, -100), Point (-100, 100))); diff --git a/extras/juce demo/Source/demos/WidgetsDemo.cpp b/extras/juce demo/Source/demos/WidgetsDemo.cpp index 337edd22c6..2ed0869065 100644 --- a/extras/juce demo/Source/demos/WidgetsDemo.cpp +++ b/extras/juce demo/Source/demos/WidgetsDemo.cpp @@ -1408,7 +1408,7 @@ public: void sliderValueChanged (Slider*) { - // When you move the roation slider, we'll apply a rotaion transform to the whole tabs component.. + // When you move the rotation slider, we'll apply a rotaion transform to the whole tabs component.. tabs.setTransform (AffineTransform::rotation ((float) (transformSlider.getValue() / (180.0 / double_Pi)), getWidth() * 0.5f, getHeight() * 0.5f)); } diff --git a/src/events/juce_AsyncUpdater.cpp b/src/events/juce_AsyncUpdater.cpp index 9d3bf49dc1..d4200beff5 100644 --- a/src/events/juce_AsyncUpdater.cpp +++ b/src/events/juce_AsyncUpdater.cpp @@ -62,7 +62,7 @@ AsyncUpdater::~AsyncUpdater() // pending on the main event thread - that's pretty dodgy threading, as the callback could // happen after this destructor has finished. You should either use a MessageManagerLock while // deleting this object, or find some other way to avoid such a race condition. - jassert (/*(! isUpdatePending()) ||*/ MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); pendingMessage = 0; } diff --git a/src/events/juce_MessageManager.cpp b/src/events/juce_MessageManager.cpp index 77ea690925..480a72d09c 100644 --- a/src/events/juce_MessageManager.cpp +++ b/src/events/juce_MessageManager.cpp @@ -60,10 +60,7 @@ MessageManager::~MessageManager() throw() doPlatformSpecificShutdown(); - /* If you hit this assertion, then you've probably leaked some kind of MessageListener object. - This could also be caused by leaking AsyncUpdaters, ChangeBroadcasters, and various other types - of event class. - */ + // If you hit this assertion, then you've probably leaked some kind of MessageListener object.. jassert (messageListeners.size() == 0); jassert (instance == this); diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index ba9413c3ea..9fe144a0b6 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -1197,6 +1197,10 @@ bool Component::isTransformed() const throw() void Component::setTransform (const AffineTransform& newTransform) { + // If you pass in a transform with no inverse, the component will have no dimensions, + // and there will be all sorts of maths errors when converting coordinates. + jassert (! newTransform.isSingularity()); + if (newTransform.isIdentity()) { if (affineTransform_ != 0) @@ -1204,6 +1208,8 @@ void Component::setTransform (const AffineTransform& newTransform) repaint(); affineTransform_ = 0; repaint(); + + sendMovedResizedMessages (false, false); } } else if (affineTransform_ == 0) @@ -1211,12 +1217,14 @@ void Component::setTransform (const AffineTransform& newTransform) repaint(); affineTransform_ = new AffineTransform (newTransform); repaint(); + sendMovedResizedMessages (false, false); } else if (*affineTransform_ != newTransform) { repaint(); *affineTransform_ = newTransform; repaint(); + sendMovedResizedMessages (false, false); } } @@ -2064,6 +2072,12 @@ const Rectangle Component::getLocalBounds() const throw() return Rectangle (getWidth(), getHeight()); } +const Rectangle Component::getBoundsInParent() const throw() +{ + return affineTransform_ == 0 ? bounds_ + : bounds_.toFloat().transformed (*affineTransform_).getSmallestIntegerContainer(); +} + void Component::getVisibleArea (RectangleList& result, const bool includeSiblings) const { result.clear(); diff --git a/src/gui/components/juce_Component.h b/src/gui/components/juce_Component.h index d0493bd161..6d3236a2b7 100644 --- a/src/gui/components/juce_Component.h +++ b/src/gui/components/juce_Component.h @@ -318,6 +318,15 @@ public: */ const Rectangle getLocalBounds() const throw(); + /** Returns the area of this component's parent which this component covers. + + The returned area is relative to the parent's coordinate space. + If the component has an affine transform specified, then the resulting area will be + the smallest rectangle that fully covers the component's transformed bounding box. + If this component has no parent, the return value will simply be the same as getBounds(). + */ + const Rectangle getBoundsInParent() const throw(); + /** Returns the region of this component that's not obscured by other, opaque components. The RectangleList that is returned represents the area of this component @@ -1291,7 +1300,18 @@ public: */ virtual void enablementChanged(); + /** Changes the transparency of this component. + When painted, the entire component and all its children will be rendered + with this as the overall opacity level, where 0 is completely invisible, and + 1.0 is fully opaque (i.e. normal). + + @see getAlpha + */ void setAlpha (float newAlpha); + + /** Returns the component's current transparancy level. + See setAlpha() for more details. + */ float getAlpha() const; //============================================================================== diff --git a/src/gui/components/menus/juce_MenuBarComponent.cpp b/src/gui/components/menus/juce_MenuBarComponent.cpp index d9bd35552c..292593261c 100644 --- a/src/gui/components/menus/juce_MenuBarComponent.cpp +++ b/src/gui/components/menus/juce_MenuBarComponent.cpp @@ -215,8 +215,8 @@ void MenuBarComponent::showMenu (int index) const Rectangle itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight()); - m.showMenu (itemPos + getScreenPosition(), - 0, itemPos.getWidth(), 0, 0, true, this, + m.showMenu (localAreaToGlobal (itemPos), + 0, itemPos.getWidth(), 0, 0, this, new AsyncCallback (this, index)); } } diff --git a/src/gui/components/menus/juce_PopupMenu.cpp b/src/gui/components/menus/juce_PopupMenu.cpp index fdea599194..ba3b48ca1b 100644 --- a/src/gui/components/menus/juce_PopupMenu.cpp +++ b/src/gui/components/menus/juce_PopupMenu.cpp @@ -1467,7 +1467,6 @@ int PopupMenu::showMenu (const Rectangle& target, const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, - const bool alignToRectangle, Component* const componentAttachedTo, ModalComponentManager::Callback* userCallback) { @@ -1482,7 +1481,7 @@ int PopupMenu::showMenu (const Rectangle& target, callback->component = Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), 0, target, minimumWidth, maximumNumColumns > 0 ? maximumNumColumns : 7, - standardItemHeight, alignToRectangle, itemIdThatMustBeVisible, + standardItemHeight, ! target.isEmpty(), itemIdThatMustBeVisible, &callback->managerOfChosenCommand, componentAttachedTo); if (callback->component == 0) @@ -1518,23 +1517,20 @@ int PopupMenu::show (const int itemIdThatMustBeVisible, const int standardItemHeight, ModalComponentManager::Callback* callback) { - const Point mousePos (Desktop::getMousePosition()); - - return showAt (mousePos.getX(), mousePos.getY(), - itemIdThatMustBeVisible, - minimumWidth, maximumNumColumns, - standardItemHeight, callback); + return showMenu (Rectangle().withPosition (Desktop::getMousePosition()), + itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, + standardItemHeight, 0, callback); } -int PopupMenu::showAt (const int screenX, const int screenY, +int PopupMenu::showAt (const Rectangle& screenAreaToAttachTo, const int itemIdThatMustBeVisible, const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { - return showMenu (Rectangle (screenX, screenY, 1, 1), + return showMenu (screenAreaToAttachTo, itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, - standardItemHeight, false, 0, callback); + standardItemHeight, 0, callback); } int PopupMenu::showAt (Component* componentToAttachTo, @@ -1546,16 +1542,12 @@ int PopupMenu::showAt (Component* componentToAttachTo, if (componentToAttachTo != 0) { return showMenu (componentToAttachTo->getScreenBounds(), - itemIdThatMustBeVisible, - minimumWidth, - maximumNumColumns, - standardItemHeight, - true, componentToAttachTo, callback); + itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, + standardItemHeight, componentToAttachTo, callback); } else { - return show (itemIdThatMustBeVisible, - minimumWidth, maximumNumColumns, + return show (itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, standardItemHeight, callback); } } diff --git a/src/gui/components/menus/juce_PopupMenu.h b/src/gui/components/menus/juce_PopupMenu.h index fa027bfbeb..5b12bd72b7 100644 --- a/src/gui/components/menus/juce_PopupMenu.h +++ b/src/gui/components/menus/juce_PopupMenu.h @@ -260,14 +260,15 @@ public: This is the same as show(), but uses a specific location (in global screen co-ordinates) rather than the current mouse position. - Note that the co-ordinates don't specify the top-left of the menu - they - indicate a point of interest, and the menu will position itself nearby to - this point, trying to keep it fully on-screen. + The screenAreaToAttachTo parameter indicates a screen area to which the menu + will be adjacent. Depending on where this is, the menu will decide which edge to + attach itself to, in order to fit itself fully on-screen. If you just want to + trigger a menu at a specific point, you can pass in a rectangle of size (0, 0) + with the position that you want. @see show() */ - int showAt (int screenX, - int screenY, + int showAt (const Rectangle& screenAreaToAttachTo, int itemIdThatMustBeVisible = 0, int minimumWidth = 0, int maximumNumColumns = 0, @@ -403,14 +404,9 @@ private: void addSeparatorIfPending(); - int showMenu (const Rectangle& target, - int itemIdThatMustBeVisible, - int minimumWidth, - int maximumNumColumns, - int standardItemHeight, - bool alignToRectangle, - Component* componentAttachedTo, - ModalComponentManager::Callback* callback); + int showMenu (const Rectangle& target, int itemIdThatMustBeVisible, + int minimumWidth, int maximumNumColumns, int standardItemHeight, + Component* componentAttachedTo, ModalComponentManager::Callback* callback); }; #endif // __JUCE_POPUPMENU_JUCEHEADER__ diff --git a/src/gui/components/windows/juce_ResizableWindow.cpp b/src/gui/components/windows/juce_ResizableWindow.cpp index 62c6b53293..98941a6a3e 100644 --- a/src/gui/components/windows/juce_ResizableWindow.cpp +++ b/src/gui/components/windows/juce_ResizableWindow.cpp @@ -76,6 +76,12 @@ ResizableWindow::ResizableWindow (const String& name, ResizableWindow::~ResizableWindow() { + // Don't delete or remove the resizer components yourself! They're managed by the + // ResizableWindow, and you should leave them alone! You may have deleted them + // accidentally by careless use of deleteAllChildren()..? + jassert (resizableCorner == 0 || getIndexOfChildComponent (resizableCorner) >= 0); + jassert (resizableBorder == 0 || getIndexOfChildComponent (resizableBorder) >= 0); + resizableCorner = 0; resizableBorder = 0; contentComponent.deleteAndZero(); diff --git a/src/gui/graphics/geometry/juce_AffineTransform.cpp b/src/gui/graphics/geometry/juce_AffineTransform.cpp index a8a7a646f2..c8fdac7018 100644 --- a/src/gui/graphics/geometry/juce_AffineTransform.cpp +++ b/src/gui/graphics/geometry/juce_AffineTransform.cpp @@ -32,37 +32,21 @@ BEGIN_JUCE_NAMESPACE //============================================================================== AffineTransform::AffineTransform() throw() - : mat00 (1.0f), - mat01 (0), - mat02 (0), - mat10 (0), - mat11 (1.0f), - mat12 (0) + : mat00 (1.0f), mat01 (0), mat02 (0), + mat10 (0), mat11 (1.0f), mat12 (0) { } AffineTransform::AffineTransform (const AffineTransform& other) throw() - : mat00 (other.mat00), - mat01 (other.mat01), - mat02 (other.mat02), - mat10 (other.mat10), - mat11 (other.mat11), - mat12 (other.mat12) + : mat00 (other.mat00), mat01 (other.mat01), mat02 (other.mat02), + mat10 (other.mat10), mat11 (other.mat11), mat12 (other.mat12) { } -AffineTransform::AffineTransform (const float mat00_, - const float mat01_, - const float mat02_, - const float mat10_, - const float mat11_, - const float mat12_) throw() - : mat00 (mat00_), - mat01 (mat01_), - mat02 (mat02_), - mat10 (mat10_), - mat11 (mat11_), - mat12 (mat12_) +AffineTransform::AffineTransform (const float mat00_, const float mat01_, const float mat02_, + const float mat10_, const float mat11_, const float mat12_) throw() + : mat00 (mat00_), mat01 (mat01_), mat02 (mat02_), + mat10 (mat10_), mat11 (mat11_), mat12 (mat12_) { } @@ -117,31 +101,13 @@ const AffineTransform AffineTransform::followedBy (const AffineTransform& other) other.mat10 * mat02 + other.mat11 * mat12 + other.mat12); } -const AffineTransform AffineTransform::followedBy (const float omat00, - const float omat01, - const float omat02, - const float omat10, - const float omat11, - const float omat12) const throw() -{ - return AffineTransform (omat00 * mat00 + omat01 * mat10, - omat00 * mat01 + omat01 * mat11, - omat00 * mat02 + omat01 * mat12 + omat02, - omat10 * mat00 + omat11 * mat10, - omat10 * mat01 + omat11 * mat11, - omat10 * mat02 + omat11 * mat12 + omat12); -} - -//============================================================================== -const AffineTransform AffineTransform::translated (const float dx, - const float dy) const throw() +const AffineTransform AffineTransform::translated (const float dx, const float dy) const throw() { return AffineTransform (mat00, mat01, mat02 + dx, mat10, mat11, mat12 + dy); } -const AffineTransform AffineTransform::translation (const float dx, - const float dy) throw() +const AffineTransform AffineTransform::translation (const float dx, const float dy) throw() { return AffineTransform (1.0f, 0, dx, 0, 1.0f, dy); @@ -152,8 +118,12 @@ const AffineTransform AffineTransform::rotated (const float rad) const throw() const float cosRad = std::cos (rad); const float sinRad = std::sin (rad); - return followedBy (cosRad, -sinRad, 0, - sinRad, cosRad, 0); + return AffineTransform (cosRad * mat00 + -sinRad * mat10, + cosRad * mat01 + -sinRad * mat11, + cosRad * mat02 + -sinRad * mat12, + sinRad * mat00 + cosRad * mat10, + sinRad * mat01 + cosRad * mat11, + sinRad * mat02 + cosRad * mat12); } const AffineTransform AffineTransform::rotation (const float rad) throw() @@ -165,33 +135,27 @@ const AffineTransform AffineTransform::rotation (const float rad) throw() sinRad, cosRad, 0); } -const AffineTransform AffineTransform::rotated (const float angle, - const float pivotX, - const float pivotY) const throw() +const AffineTransform AffineTransform::rotation (const float rad, const float pivotX, const float pivotY) throw() { - return translated (-pivotX, -pivotY) - .rotated (angle) - .translated (pivotX, pivotY); + const float cosRad = std::cos (rad); + const float sinRad = std::sin (rad); + + return AffineTransform (cosRad, -sinRad, -cosRad * pivotX + sinRad * pivotY + pivotX, + sinRad, cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY); } -const AffineTransform AffineTransform::rotation (const float angle, - const float pivotX, - const float pivotY) throw() +const AffineTransform AffineTransform::rotated (const float angle, const float pivotX, const float pivotY) const throw() { - return translation (-pivotX, -pivotY) - .rotated (angle) - .translated (pivotX, pivotY); + return followedBy (rotation (angle, pivotX, pivotY)); } -const AffineTransform AffineTransform::scaled (const float factorX, - const float factorY) const throw() +const AffineTransform AffineTransform::scaled (const float factorX, const float factorY) const throw() { return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02, factorY * mat10, factorY * mat11, factorY * mat12); } -const AffineTransform AffineTransform::scale (const float factorX, - const float factorY) throw() +const AffineTransform AffineTransform::scale (const float factorX, const float factorY) throw() { return AffineTransform (factorX, 0, 0, 0, factorY, 0); @@ -200,9 +164,8 @@ const AffineTransform AffineTransform::scale (const float factorX, const AffineTransform AffineTransform::scaled (const float factorX, const float factorY, const float pivotX, const float pivotY) const throw() { - return translated (-pivotX, -pivotY) - .scaled (factorX, factorY) - .translated (pivotX, pivotY); + return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX), + factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY)); } const AffineTransform AffineTransform::scale (const float factorX, const float factorY, @@ -212,11 +175,20 @@ const AffineTransform AffineTransform::scale (const float factorX, const float f 0, factorY, pivotY * (1.0f - factorY)); } -const AffineTransform AffineTransform::sheared (const float shearX, - const float shearY) const throw() +const AffineTransform AffineTransform::shear (float shearX, float shearY) throw() { - return followedBy (1.0f, shearX, 0, - shearY, 1.0f, 0); + return AffineTransform (1.0f, shearX, 0, + shearY, 1.0f, 0); +} + +const AffineTransform AffineTransform::sheared (const float shearX, const float shearY) const throw() +{ + return AffineTransform (mat00 + shearX * mat10, + mat01 + shearX * mat11, + mat02 + shearX * mat12, + shearY * mat00 + mat10, + shearY * mat01 + mat11, + shearY * mat02 + mat12); } const AffineTransform AffineTransform::inverted() const throw() diff --git a/src/gui/graphics/geometry/juce_AffineTransform.h b/src/gui/graphics/geometry/juce_AffineTransform.h index d20a942b5d..0be3c4b176 100644 --- a/src/gui/graphics/geometry/juce_AffineTransform.h +++ b/src/gui/graphics/geometry/juce_AffineTransform.h @@ -178,11 +178,12 @@ public: float pivotX, float pivotY) throw(); /** Returns a transform which is the same as this one followed by a shear. - The shear is centred around the origin (0, 0). */ - const AffineTransform sheared (float shearX, - float shearY) const throw(); + const AffineTransform sheared (float shearX, float shearY) const throw(); + + /** Returns a shear transform, centred around the origin (0, 0). */ + static const AffineTransform shear (float shearX, float shearY) throw(); /** Returns a matrix which is the inverse operation of this one. @@ -244,11 +245,6 @@ public: //============================================================================== juce_UseDebuggingNewOperator - -private: - //============================================================================== - const AffineTransform followedBy (float mat00, float mat01, float mat02, - float mat10, float mat11, float mat12) const throw(); }; #endif // __JUCE_AFFINETRANSFORM_JUCEHEADER__ diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp index 4c2ea2ea68..5a2fb6e02c 100644 --- a/src/native/windows/juce_win32_Windowing.cpp +++ b/src/native/windows/juce_win32_Windowing.cpp @@ -2630,6 +2630,11 @@ void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStan DestroyCursor ((HCURSOR) cursorHandle); } +enum +{ + hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor) +}; + void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type) { LPCTSTR cursorName = IDC_ARROW; @@ -2637,7 +2642,7 @@ void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorT switch (type) { case NormalCursor: break; - case NoCursor: return 0; + case NoCursor: return (void*) hiddenMouseCursorHandle; case WaitCursor: cursorName = IDC_WAIT; break; case IBeamCursor: cursorName = IDC_IBEAM; break; case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; @@ -2696,6 +2701,8 @@ void MouseCursor::showInWindow (ComponentPeer*) const if (c == 0) c = LoadCursor (0, IDC_ARROW); + else if (c == (HCURSOR) hiddenMouseCursorHandle) + c = 0; SetCursor (c); } diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp index 38aacb782b..7977e8a9ba 100644 --- a/src/text/juce_XmlDocument.cpp +++ b/src/text/juce_XmlDocument.cpp @@ -208,6 +208,23 @@ void XmlDocument::skipHeader() if (input == 0) return; + #if JUCE_DEBUG + const String header (found, input - found); + const String encoding (header.fromFirstOccurrenceOf ("encoding", false, true) + .fromFirstOccurrenceOf ("=", false, false) + .fromFirstOccurrenceOf ("\"", false, false) + .upToFirstOccurrenceOf ("\"", false, false).trim()); + + /* If you load an XML document with a non-UTF encoding type, it may have been + loaded wrongly.. Since all the files are read via the normal juce file streams, + they're treated as UTF-8, so by the time it gets to the parser, the encoding will + have been lost. Best plan is to stick to utf-8 or if you have specific files to + read, use your own code to convert them to a unicode String, and pass that to the + XML parser. + */ + jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-")); + #endif + input += 2; }