| @@ -196,44 +196,64 @@ struct Component::ComponentHelpers | |||
| static inline bool hitTest (Component& comp, Point<int> localPoint) | |||
| { | |||
| return isPositiveAndBelow (localPoint.x, comp.getWidth()) | |||
| && isPositiveAndBelow (localPoint.y, comp.getHeight()) | |||
| && comp.hitTest (localPoint.x, localPoint.y); | |||
| && isPositiveAndBelow (localPoint.y, comp.getHeight()) | |||
| && comp.hitTest (localPoint.x, localPoint.y); | |||
| } | |||
| static Point<int> convertFromParentSpace (const Component& comp, Point<int> pointInParentSpace) | |||
| template <typename PointOrRect> | |||
| static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept | |||
| { | |||
| if (comp.affineTransform == nullptr) | |||
| return pointInParentSpace - comp.getPosition(); | |||
| const float scale = Desktop::getInstance().masterScaleFactor; | |||
| return scale != 1.0f ? pos / scale : pos; | |||
| } | |||
| return pointInParentSpace.toFloat().transformedBy (comp.affineTransform->inverted()).toInt() - comp.getPosition(); | |||
| template <typename PointOrRect> | |||
| static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept | |||
| { | |||
| const float scale = Desktop::getInstance().masterScaleFactor; | |||
| return scale != 1.0f ? pos * scale : pos; | |||
| } | |||
| static Rectangle<int> convertFromParentSpace (const Component& comp, const Rectangle<int>& areaInParentSpace) | |||
| // converts an unscaled position within a peer to the local position within that peer's component | |||
| template <typename PointOrRect> | |||
| static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept | |||
| { | |||
| if (comp.affineTransform == nullptr) | |||
| return areaInParentSpace - comp.getPosition(); | |||
| if (comp.isTransformed()) | |||
| pos = pos.transformedBy (comp.getTransform().inverted()); | |||
| return unscaledScreenPosToScaled (pos); | |||
| } | |||
| // converts a position within a peer's component to the unscaled position within the peer | |||
| template <typename PointOrRect> | |||
| static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept | |||
| { | |||
| if (comp.isTransformed()) | |||
| pos = pos.transformedBy (comp.getTransform()); | |||
| return areaInParentSpace.toFloat().transformedBy (comp.affineTransform->inverted()).getSmallestIntegerContainer() - comp.getPosition(); | |||
| return scaledScreenPosToUnscaled (pos); | |||
| } | |||
| static Point<int> convertToParentSpace (const Component& comp, Point<int> pointInLocalSpace) | |||
| template <typename PointOrRect> | |||
| static PointOrRect convertFromParentSpace (const Component& comp, PointOrRect pointInParentSpace) | |||
| { | |||
| if (comp.affineTransform == nullptr) | |||
| return pointInLocalSpace + comp.getPosition(); | |||
| return pointInParentSpace - comp.getPosition(); | |||
| return (pointInLocalSpace + comp.getPosition()).toFloat().transformedBy (*comp.affineTransform).toInt(); | |||
| return pointInParentSpace.transformedBy (comp.affineTransform->inverted()) - comp.getPosition(); | |||
| } | |||
| static Rectangle<int> convertToParentSpace (const Component& comp, const Rectangle<int>& areaInLocalSpace) | |||
| template <typename PointOrRect> | |||
| static PointOrRect convertToParentSpace (const Component& comp, PointOrRect pointInLocalSpace) | |||
| { | |||
| if (comp.affineTransform == nullptr) | |||
| return areaInLocalSpace + comp.getPosition(); | |||
| return pointInLocalSpace + comp.getPosition(); | |||
| return (areaInLocalSpace + comp.getPosition()).transformedBy (*comp.affineTransform); | |||
| return (pointInLocalSpace + comp.getPosition()).transformedBy (*comp.affineTransform); | |||
| } | |||
| template <typename Type> | |||
| static Type convertFromDistantParentSpace (const Component* parent, const Component& target, const Type& coordInParent) | |||
| template <typename PointOrRect> | |||
| static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, const PointOrRect& coordInParent) | |||
| { | |||
| const Component* const directParent = target.getParentComponent(); | |||
| jassert (directParent != nullptr); | |||
| @@ -244,8 +264,8 @@ struct Component::ComponentHelpers | |||
| return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent)); | |||
| } | |||
| template <typename Type> | |||
| static Type convertCoordinate (const Component* target, const Component* source, Type p) | |||
| template <typename PointOrRect> | |||
| static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) | |||
| { | |||
| while (source != nullptr) | |||
| { | |||
| @@ -255,19 +275,8 @@ struct Component::ComponentHelpers | |||
| if (source->isParentOf (target)) | |||
| return convertFromDistantParentSpace (source, *target, p); | |||
| if (source->isOnDesktop()) | |||
| { | |||
| if (source->isTransformed()) | |||
| p = p.transformedBy (source->getTransform()); | |||
| p = source->getPeer()->localToGlobal (p); | |||
| source = nullptr; | |||
| } | |||
| else | |||
| { | |||
| p = convertToParentSpace (*source, p); | |||
| source = source->getParentComponent(); | |||
| } | |||
| p = convertToParentSpace (*source, p); | |||
| source = source->getParentComponent(); | |||
| } | |||
| jassert (source == nullptr); | |||
| @@ -276,17 +285,7 @@ struct Component::ComponentHelpers | |||
| const Component* const topLevelComp = target->getTopLevelComponent(); | |||
| if (topLevelComp->isOnDesktop()) | |||
| { | |||
| p = topLevelComp->getPeer()->globalToLocal (p); | |||
| if (topLevelComp->isTransformed()) | |||
| p = p.transformedBy (topLevelComp->getTransform().inverted()); | |||
| } | |||
| else | |||
| { | |||
| p = convertFromParentSpace (*topLevelComp, p); | |||
| } | |||
| p = convertFromParentSpace (*topLevelComp, p); | |||
| if (topLevelComp == target) | |||
| return p; | |||
| @@ -1218,11 +1217,6 @@ void Component::setBoundsToFit (int x, int y, int width, int height, | |||
| } | |||
| //============================================================================== | |||
| bool Component::isTransformed() const noexcept | |||
| { | |||
| return affineTransform != nullptr; | |||
| } | |||
| void Component::setTransform (const AffineTransform& newTransform) | |||
| { | |||
| // If you pass in a transform with no inverse, the component will have no dimensions, | |||
| @@ -1256,6 +1250,11 @@ void Component::setTransform (const AffineTransform& newTransform) | |||
| } | |||
| } | |||
| bool Component::isTransformed() const noexcept | |||
| { | |||
| return affineTransform != nullptr; | |||
| } | |||
| AffineTransform Component::getTransform() const | |||
| { | |||
| return affineTransform != nullptr ? *affineTransform : AffineTransform::identity; | |||
| @@ -1802,8 +1801,8 @@ void Component::internalRepaintUnchecked (const Rectangle<int>& area, const bool | |||
| CHECK_MESSAGE_MANAGER_IS_LOCKED | |||
| if (ComponentPeer* const peer = getPeer()) | |||
| peer->repaint (affineTransform != nullptr ? area.transformedBy (*affineTransform) | |||
| : area); | |||
| peer->repaint (ComponentHelpers::scaledScreenPosToUnscaled (affineTransform != nullptr ? area.transformedBy (*affineTransform) | |||
| : area)); | |||
| } | |||
| else | |||
| { | |||
| @@ -142,18 +142,6 @@ void Desktop::componentBroughtToFront (Component* const c) | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Desktop::addPeer (ComponentPeer* peer) | |||
| { | |||
| peers.add (peer); | |||
| } | |||
| void Desktop::removePeer (ComponentPeer* peer) | |||
| { | |||
| peers.removeFirstMatchingValue (peer); | |||
| triggerFocusCallback(); | |||
| } | |||
| //============================================================================== | |||
| Point<int> Desktop::getMousePosition() | |||
| { | |||
| @@ -489,3 +477,13 @@ bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const | |||
| return (allowedOrientations & orientation) != 0; | |||
| } | |||
| void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept | |||
| { | |||
| if (masterScaleFactor != newScaleFactor) | |||
| { | |||
| masterScaleFactor = newScaleFactor; | |||
| displays->refresh(); | |||
| } | |||
| } | |||
| @@ -375,7 +375,18 @@ public: | |||
| void findDisplays (float masterScale); | |||
| }; | |||
| const Displays& getDisplays() const noexcept { return *displays; } | |||
| const Displays& getDisplays() const noexcept { return *displays; } | |||
| //============================================================================== | |||
| /** Sets a global scale factor to be used for all desktop windows. | |||
| Setting this will also scale the monitor sizes that are returned by getDisplays(). | |||
| */ | |||
| void setGlobalScaleFactor (float newScaleFactor) noexcept; | |||
| /** Returns the current global scale factor, as set by setGlobalScaleFactor(). | |||
| @see setGlobalScaleFactor | |||
| */ | |||
| float getGlobalScaleFactor() const noexcept { return masterScaleFactor; } | |||
| //============================================================================== | |||
| /** True if the OS supports semitransparent windows */ | |||
| @@ -401,9 +412,6 @@ private: | |||
| Array <Component*> desktopComponents; | |||
| Array <ComponentPeer*> peers; | |||
| void addPeer (ComponentPeer*); | |||
| void removePeer (ComponentPeer*); | |||
| ScopedPointer<Displays> displays; | |||
| Point<int> lastFakeMouseMove; | |||
| @@ -123,18 +123,8 @@ bool FileChooserDialogBox::show (int w, int h) | |||
| bool FileChooserDialogBox::showAt (int x, int y, int w, int h) | |||
| { | |||
| if (w <= 0) | |||
| { | |||
| Component* const previewComp = content->chooserComponent.getPreviewComponent(); | |||
| if (previewComp != nullptr) | |||
| w = 400 + previewComp->getWidth(); | |||
| else | |||
| w = 600; | |||
| } | |||
| if (h <= 0) | |||
| h = 500; | |||
| if (w <= 0) w = getDefaultWidth(); | |||
| if (h <= 0) h = 500; | |||
| if (x < 0 || y < 0) | |||
| centreWithSize (w, h); | |||
| @@ -149,11 +139,15 @@ bool FileChooserDialogBox::showAt (int x, int y, int w, int h) | |||
| void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround) | |||
| { | |||
| Component* const previewComp = content->chooserComponent.getPreviewComponent(); | |||
| centreAroundComponent (componentToCentreAround, getDefaultWidth(), 500); | |||
| } | |||
| int FileChooserDialogBox::getDefaultWidth() const | |||
| { | |||
| if (Component* const previewComp = content->chooserComponent.getPreviewComponent()) | |||
| return 400 + previewComp->getWidth(); | |||
| centreAroundComponent (componentToCentreAround, | |||
| previewComp != nullptr ? 400 + previewComp->getWidth() : 600, | |||
| 500); | |||
| return 600; | |||
| } | |||
| //============================================================================== | |||
| @@ -146,6 +146,7 @@ private: | |||
| void fileClicked (const File&, const MouseEvent&) override; | |||
| void fileDoubleClicked (const File&) override; | |||
| void browserRootChanged (const File&) override; | |||
| int getDefaultWidth() const; | |||
| void okButtonPressed(); | |||
| void createNewFolder(); | |||
| @@ -57,12 +57,18 @@ public: | |||
| return lastPeer; | |||
| } | |||
| static Point<int> screenPosToLocalPos (Component& comp, Point<int> pos) | |||
| { | |||
| return comp.getLocalPoint (nullptr, Component::ComponentHelpers::unscaledScreenPosToScaled (pos)); | |||
| } | |||
| Component* findComponentAt (Point<int> screenPos) | |||
| { | |||
| if (ComponentPeer* const peer = getPeer()) | |||
| { | |||
| Component& comp = peer->getComponent(); | |||
| const Point<int> relativePos (comp.getLocalPoint (nullptr, screenPos)); | |||
| Point<int> relativePos (Component::ComponentHelpers::convertFromParentSpace (comp, | |||
| Component::ComponentHelpers::unscaledScreenPosToScaled (screenPos))); | |||
| // (the contains() call is needed to test for overlapping desktop windows) | |||
| if (comp.contains (relativePos)) | |||
| @@ -76,20 +82,21 @@ public: | |||
| { | |||
| // This needs to return the live position if possible, but it mustn't update the lastScreenPos | |||
| // value, because that can cause continuity problems. | |||
| return unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentRawMousePosition() | |||
| : lastScreenPos); | |||
| return Component::ComponentHelpers::unscaledScreenPosToScaled | |||
| (unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentRawMousePosition() | |||
| : lastScreenPos)); | |||
| } | |||
| void setScreenPosition (Point<int> p) | |||
| { | |||
| MouseInputSource::setRawMousePosition (p); | |||
| MouseInputSource::setRawMousePosition (Component::ComponentHelpers::scaledScreenPosToUnscaled (p)); | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_DUMP_MOUSE_EVENTS | |||
| #define JUCE_MOUSE_EVENT_DBG(desc) DBG ("Mouse " desc << " #" << source.getIndex() \ | |||
| << ": " << comp->getLocalPoint (nullptr, screenPos).toString() \ | |||
| << " - Comp: " << String::toHexString ((int) comp)); | |||
| << ": " << screenPosToLocalPos (comp, screenPos).toString() \ | |||
| << " - Comp: " << String::toHexString ((int) &comp)); | |||
| #else | |||
| #define JUCE_MOUSE_EVENT_DBG(desc) | |||
| #endif | |||
| @@ -97,49 +104,49 @@ public: | |||
| void sendMouseEnter (Component& comp, Point<int> screenPos, Time time) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("enter") | |||
| comp.internalMouseEnter (source, comp.getLocalPoint (nullptr, screenPos), time); | |||
| comp.internalMouseEnter (source, screenPosToLocalPos (comp, screenPos), time); | |||
| } | |||
| void sendMouseExit (Component& comp, Point<int> screenPos, Time time) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("exit") | |||
| comp.internalMouseExit (source, comp.getLocalPoint (nullptr, screenPos), time); | |||
| comp.internalMouseExit (source, screenPosToLocalPos (comp, screenPos), time); | |||
| } | |||
| void sendMouseMove (Component& comp, Point<int> screenPos, Time time) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("move") | |||
| comp.internalMouseMove (source, comp.getLocalPoint (nullptr, screenPos), time); | |||
| comp.internalMouseMove (source, screenPosToLocalPos (comp, screenPos), time); | |||
| } | |||
| void sendMouseDown (Component& comp, Point<int> screenPos, Time time) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("down") | |||
| comp.internalMouseDown (source, comp.getLocalPoint (nullptr, screenPos), time); | |||
| comp.internalMouseDown (source, screenPosToLocalPos (comp, screenPos), time); | |||
| } | |||
| void sendMouseDrag (Component& comp, Point<int> screenPos, Time time) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("drag") | |||
| comp.internalMouseDrag (source, comp.getLocalPoint (nullptr, screenPos), time); | |||
| comp.internalMouseDrag (source, screenPosToLocalPos (comp, screenPos), time); | |||
| } | |||
| void sendMouseUp (Component& comp, Point<int> screenPos, Time time, const ModifierKeys oldMods) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("up") | |||
| comp.internalMouseUp (source, comp.getLocalPoint (nullptr, screenPos), time, oldMods); | |||
| comp.internalMouseUp (source, screenPosToLocalPos (comp, screenPos), time, oldMods); | |||
| } | |||
| void sendMouseWheel (Component& comp, Point<int> screenPos, Time time, const MouseWheelDetails& wheel) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("wheel") | |||
| comp.internalMouseWheel (source, comp.getLocalPoint (nullptr, screenPos), time, wheel); | |||
| comp.internalMouseWheel (source, screenPosToLocalPos (comp, screenPos), time, wheel); | |||
| } | |||
| void sendMagnifyGesture (Component& comp, Point<int> screenPos, Time time, const float amount) | |||
| { | |||
| JUCE_MOUSE_EVENT_DBG ("magnify") | |||
| comp.internalMagnifyGesture (source, comp.getLocalPoint (nullptr, screenPos), time, amount); | |||
| comp.internalMagnifyGesture (source, screenPosToLocalPos (comp, screenPos), time, amount); | |||
| } | |||
| //============================================================================== | |||
| @@ -330,7 +337,7 @@ public: | |||
| //============================================================================== | |||
| Time getLastMouseDownTime() const noexcept { return mouseDowns[0].time; } | |||
| Point<int> getLastMouseDownPosition() const noexcept { return mouseDowns[0].position; } | |||
| Point<int> getLastMouseDownPosition() const noexcept { return Component::ComponentHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); } | |||
| int getNumberOfMultipleClicks() const noexcept | |||
| { | |||
| @@ -33,12 +33,14 @@ ComponentPeer::ComponentPeer (Component& comp, const int flags) | |||
| uniqueID (lastUniqueID += 2), // increment by 2 so that this can never hit 0 | |||
| isWindowMinimised (false) | |||
| { | |||
| Desktop::getInstance().addPeer (this); | |||
| Desktop::getInstance().peers.add (this); | |||
| } | |||
| ComponentPeer::~ComponentPeer() | |||
| { | |||
| Desktop::getInstance().removePeer (this); | |||
| Desktop& desktop = Desktop::getInstance(); | |||
| desktop.peers.removeFirstMatchingValue (this); | |||
| desktop.triggerFocusCallback(); | |||
| } | |||
| //============================================================================== | |||
| @@ -74,7 +76,7 @@ bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept | |||
| void ComponentPeer::updateBounds() | |||
| { | |||
| setBounds (component.getBoundsInParent(), false); | |||
| setBounds (Component::ComponentHelpers::scaledScreenPosToUnscaled (component.getBoundsInParent()), false); | |||
| } | |||
| //============================================================================== | |||
| @@ -105,7 +107,14 @@ void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo) | |||
| ModifierKeys::updateCurrentModifiers(); | |||
| Graphics g (&contextToPaintTo); | |||
| g.addTransform (component.getTransform()); | |||
| if (component.isTransformed()) | |||
| g.addTransform (component.getTransform()); | |||
| float masterScale = Desktop::getInstance().masterScaleFactor; | |||
| if (masterScale != 1.0f) | |||
| g.addTransform (AffineTransform::scale (masterScale)); | |||
| #if JUCE_ENABLE_REPAINT_DEBUGGING | |||
| g.saveState(); | |||
| @@ -284,12 +293,18 @@ void ComponentPeer::handleMovedOrResized() | |||
| { | |||
| const WeakReference<Component> deletionChecker (&component); | |||
| const Rectangle<int> newBounds (getBounds().transformedBy (component.getTransform().inverted())); | |||
| const bool wasMoved = (component.getPosition() != newBounds.getPosition()); | |||
| const bool wasResized = (component.getWidth() != newBounds.getWidth() || component.getHeight() != newBounds.getHeight()); | |||
| Rectangle<int> newBounds (getBounds()); | |||
| Rectangle<int> oldBounds (component.getBounds()); | |||
| oldBounds = Component::ComponentHelpers::localPositionToRawPeerPos (component, oldBounds); | |||
| const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition()); | |||
| const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight()); | |||
| if (wasMoved || wasResized) | |||
| { | |||
| newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, newBounds); | |||
| component.bounds = newBounds; | |||
| if (wasResized) | |||