This new factoring is a bit more typesafe, as it avoids casting internals to void*. It also allows cursors to scale appropriately on high resolution displays on Windows.v6.1.6
@@ -124,7 +124,20 @@ namespace juce | |||||
} | } | ||||
bool isWindowOnCurrentVirtualDesktop (void*); | bool isWindowOnCurrentVirtualDesktop (void*); | ||||
} | |||||
struct CustomMouseCursorInfo | |||||
{ | |||||
CustomMouseCursorInfo() = default; | |||||
CustomMouseCursorInfo (const Image& im, Point<int> hs, float scale = 1.0f) noexcept | |||||
: image (im), hotspot (hs), scaleFactor (scale) | |||||
{} | |||||
Image image; | |||||
Point<int> hotspot; | |||||
float scaleFactor = 1.0f; | |||||
}; | |||||
} // namespace juce | |||||
#include "accessibility/juce_AccessibilityHandler.cpp" | #include "accessibility/juce_AccessibilityHandler.cpp" | ||||
#include "components/juce_Component.cpp" | #include "components/juce_Component.cpp" | ||||
@@ -136,7 +149,6 @@ namespace juce | |||||
#include "components/juce_ModalComponentManager.cpp" | #include "components/juce_ModalComponentManager.cpp" | ||||
#include "mouse/juce_ComponentDragger.cpp" | #include "mouse/juce_ComponentDragger.cpp" | ||||
#include "mouse/juce_DragAndDropContainer.cpp" | #include "mouse/juce_DragAndDropContainer.cpp" | ||||
#include "mouse/juce_MouseCursor.cpp" | |||||
#include "mouse/juce_MouseEvent.cpp" | #include "mouse/juce_MouseEvent.cpp" | ||||
#include "mouse/juce_MouseInactivityDetector.cpp" | #include "mouse/juce_MouseInactivityDetector.cpp" | ||||
#include "mouse/juce_MouseListener.cpp" | #include "mouse/juce_MouseListener.cpp" | ||||
@@ -428,3 +440,6 @@ bool juce::isWindowOnCurrentVirtualDesktop (void* x) | |||||
juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } | juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } | ||||
juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} | juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} | ||||
#endif | #endif | ||||
// Depends on types defined in platform-specific windowing files | |||||
#include "mouse/juce_MouseCursor.cpp" |
@@ -26,34 +26,21 @@ | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
struct CustomMouseCursorInfo | |||||
{ | |||||
CustomMouseCursorInfo (const Image& im, Point<int> hs, float scale = 1.0f) noexcept | |||||
: image (im), hotspot (hs), scaleFactor (scale) | |||||
{} | |||||
void* create() const; | |||||
Image image; | |||||
const Point<int> hotspot; | |||||
const float scaleFactor; | |||||
JUCE_DECLARE_NON_COPYABLE (CustomMouseCursorInfo) | |||||
}; | |||||
class MouseCursor::SharedCursorHandle | class MouseCursor::SharedCursorHandle | ||||
{ | { | ||||
public: | public: | ||||
explicit SharedCursorHandle (const MouseCursor::StandardCursorType type) | explicit SharedCursorHandle (const MouseCursor::StandardCursorType type) | ||||
: handle (createStandardMouseCursor (type), Deleter { true }), | |||||
standardType (type) | |||||
: handle (type), | |||||
standardType (type), | |||||
standard (true) | |||||
{ | { | ||||
} | } | ||||
SharedCursorHandle (const Image& image, Point<int> hotSpot, float scaleFactor) | SharedCursorHandle (const Image& image, Point<int> hotSpot, float scaleFactor) | ||||
: info (std::make_unique<CustomMouseCursorInfo> (image, hotSpot, scaleFactor)), | |||||
handle (info->create(), Deleter { false }), | |||||
standardType (MouseCursor::NormalCursor) | |||||
: info { image, hotSpot, scaleFactor }, | |||||
handle (info), | |||||
standardType (MouseCursor::NormalCursor), | |||||
standard (false) | |||||
{ | { | ||||
// your hotspot needs to be within the bounds of the image! | // your hotspot needs to be within the bounds of the image! | ||||
jassert (image.getBounds().contains (hotSpot)); | jassert (image.getBounds().contains (hotSpot)); | ||||
@@ -81,23 +68,17 @@ public: | |||||
bool isStandardType (MouseCursor::StandardCursorType type) const noexcept | bool isStandardType (MouseCursor::StandardCursorType type) const noexcept | ||||
{ | { | ||||
return type == standardType && handle.get_deleter().isStandard; | |||||
return type == standardType && standard; | |||||
} | } | ||||
void* getHandle() const noexcept { return handle.get(); } | |||||
PlatformSpecificHandle* getHandle() noexcept { return &handle; } | |||||
MouseCursor::StandardCursorType getType() const noexcept { return standardType; } | MouseCursor::StandardCursorType getType() const noexcept { return standardType; } | ||||
CustomMouseCursorInfo* getCustomInfo() const noexcept { return info.get(); } | |||||
private: | private: | ||||
struct Deleter | |||||
{ | |||||
void operator() (void* ptr) const noexcept { deleteMouseCursor (ptr, isStandard); } | |||||
const bool isStandard; | |||||
}; | |||||
std::unique_ptr<CustomMouseCursorInfo> info; | |||||
std::unique_ptr<void, Deleter> handle; | |||||
CustomMouseCursorInfo info; | |||||
PlatformSpecificHandle handle; | |||||
const MouseCursor::StandardCursorType standardType; | const MouseCursor::StandardCursorType standardType; | ||||
const bool standard; | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedCursorHandle) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedCursorHandle) | ||||
}; | }; | ||||
@@ -144,11 +125,6 @@ bool MouseCursor::operator== (StandardCursorType type) const noexcept | |||||
bool MouseCursor::operator!= (const MouseCursor& other) const noexcept { return ! operator== (other); } | bool MouseCursor::operator!= (const MouseCursor& other) const noexcept { return ! operator== (other); } | ||||
bool MouseCursor::operator!= (StandardCursorType type) const noexcept { return ! operator== (type); } | bool MouseCursor::operator!= (StandardCursorType type) const noexcept { return ! operator== (type); } | ||||
void* MouseCursor::getHandle() const noexcept | |||||
{ | |||||
return cursorHandle != nullptr ? cursorHandle->getHandle() : nullptr; | |||||
} | |||||
void MouseCursor::showWaitCursor() | void MouseCursor::showWaitCursor() | ||||
{ | { | ||||
Desktop::getInstance().getMainMouseSource().showMouseCursor (MouseCursor::WaitCursor); | Desktop::getInstance().getMainMouseSource().showMouseCursor (MouseCursor::WaitCursor); | ||||
@@ -159,4 +135,14 @@ void MouseCursor::hideWaitCursor() | |||||
Desktop::getInstance().getMainMouseSource().revealCursor(); | Desktop::getInstance().getMainMouseSource().revealCursor(); | ||||
} | } | ||||
MouseCursor::PlatformSpecificHandle* MouseCursor::getHandle() const noexcept | |||||
{ | |||||
return cursorHandle != nullptr ? cursorHandle->getHandle() : nullptr; | |||||
} | |||||
void MouseCursor::showInWindow (ComponentPeer* peer) const | |||||
{ | |||||
PlatformSpecificHandle::showInWindow (getHandle(), peer); | |||||
} | |||||
} // namespace juce | } // namespace juce |
@@ -168,12 +168,11 @@ private: | |||||
class SharedCursorHandle; | class SharedCursorHandle; | ||||
std::shared_ptr<SharedCursorHandle> cursorHandle; | std::shared_ptr<SharedCursorHandle> cursorHandle; | ||||
class PlatformSpecificHandle; | |||||
friend class MouseInputSourceInternal; | friend class MouseInputSourceInternal; | ||||
void showInWindow (ComponentPeer*) const; | void showInWindow (ComponentPeer*) const; | ||||
void* getHandle() const noexcept; | |||||
static void* createStandardMouseCursor (MouseCursor::StandardCursorType); | |||||
static void deleteMouseCursor (void* cursorHandle, bool isStandard); | |||||
PlatformSpecificHandle* getHandle() const noexcept; | |||||
JUCE_LEAK_DETECTOR (MouseCursor) | JUCE_LEAK_DETECTOR (MouseCursor) | ||||
}; | }; | ||||
@@ -1873,12 +1873,14 @@ Image juce_createIconForFile (const File& /*file*/) | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
void* CustomMouseCursorInfo::create() const { return nullptr; } | |||||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType) { return nullptr; } | |||||
void MouseCursor::deleteMouseCursor (void* /*cursorHandle*/, bool /*isStandard*/) {} | |||||
class MouseCursor::PlatformSpecificHandle | |||||
{ | |||||
public: | |||||
PlatformSpecificHandle (const MouseCursor::StandardCursorType) {} | |||||
PlatformSpecificHandle (const CustomMouseCursorInfo&) {} | |||||
//============================================================================== | |||||
void MouseCursor::showInWindow (ComponentPeer*) const {} | |||||
static void showInWindow (PlatformSpecificHandle*, ComponentPeer*) {} | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, bool /*canMove*/, | bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, bool /*canMove*/, | ||||
@@ -648,27 +648,46 @@ void MouseInputSource::setRawMousePosition (Point<float> newPosition) | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
void* CustomMouseCursorInfo::create() const | |||||
class MouseCursor::PlatformSpecificHandle | |||||
{ | { | ||||
return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image, hotspot); | |||||
} | |||||
public: | |||||
explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type) | |||||
: cursorHandle (makeHandle (type)) {} | |||||
void MouseCursor::deleteMouseCursor (void* cursorHandle, bool) | |||||
{ | |||||
if (cursorHandle != nullptr) | |||||
XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle); | |||||
} | |||||
explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info) | |||||
: cursorHandle (makeHandle (info)) {} | |||||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) | |||||
{ | |||||
return XWindowSystem::getInstance()->createStandardMouseCursor (type); | |||||
} | |||||
~PlatformSpecificHandle() | |||||
{ | |||||
if (cursorHandle != Cursor{}) | |||||
XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle); | |||||
} | |||||
void MouseCursor::showInWindow (ComponentPeer* peer) const | |||||
{ | |||||
if (peer != nullptr) | |||||
XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), getHandle()); | |||||
} | |||||
static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer) | |||||
{ | |||||
const auto cursor = handle != nullptr ? handle->cursorHandle : Cursor{}; | |||||
if (peer != nullptr) | |||||
XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), cursor); | |||||
} | |||||
private: | |||||
static Cursor makeHandle (const CustomMouseCursorInfo& info) | |||||
{ | |||||
return XWindowSystem::getInstance()->createCustomMouseCursorInfo (info.image, info.hotspot); | |||||
} | |||||
static Cursor makeHandle (MouseCursor::StandardCursorType type) | |||||
{ | |||||
return XWindowSystem::getInstance()->createStandardMouseCursor (type); | |||||
} | |||||
Cursor cursorHandle; | |||||
//============================================================================== | |||||
JUCE_DECLARE_NON_COPYABLE (PlatformSpecificHandle) | |||||
JUCE_DECLARE_NON_MOVEABLE (PlatformSpecificHandle) | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp) | static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp) | ||||
@@ -29,8 +29,34 @@ namespace juce | |||||
#if JUCE_MAC | #if JUCE_MAC | ||||
//============================================================================== | //============================================================================== | ||||
namespace MouseCursorHelpers | |||||
class MouseCursor::PlatformSpecificHandle | |||||
{ | { | ||||
public: | |||||
PlatformSpecificHandle (const MouseCursor::StandardCursorType type) | |||||
: cursorHandle (createCursor (type)) {} | |||||
PlatformSpecificHandle (const CustomMouseCursorInfo& info) | |||||
: cursorHandle (createCursor (info)) {} | |||||
~PlatformSpecificHandle() | |||||
{ | |||||
[cursorHandle release]; | |||||
} | |||||
static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer*) | |||||
{ | |||||
auto c = [&] | |||||
{ | |||||
if (handle == nullptr || handle->cursorHandle == nullptr) | |||||
return [NSCursor arrowCursor]; | |||||
return handle->cursorHandle; | |||||
}(); | |||||
[c set]; | |||||
} | |||||
private: | |||||
static NSCursor* fromNSImage (NSImage* im, NSPoint hotspot) | static NSCursor* fromNSImage (NSImage* im, NSPoint hotspot) | ||||
{ | { | ||||
NSCursor* c = [[NSCursor alloc] initWithImage: im | NSCursor* c = [[NSCursor alloc] initWithImage: im | ||||
@@ -39,13 +65,13 @@ namespace MouseCursorHelpers | |||||
return c; | return c; | ||||
} | } | ||||
static void* fromHIServices (const char* filename) | |||||
static NSCursor* fromHIServices (const char* filename) | |||||
{ | { | ||||
JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
{ | { | ||||
auto cursorPath = String ("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/" | auto cursorPath = String ("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/" | ||||
"HIServices.framework/Versions/A/Resources/cursors/") | "HIServices.framework/Versions/A/Resources/cursors/") | ||||
+ filename; | |||||
+ filename; | |||||
NSImage* originalImage = [[NSImage alloc] initByReferencingFile: juceStringToNS (cursorPath + "/cursor.pdf")]; | NSImage* originalImage = [[NSImage alloc] initByReferencingFile: juceStringToNS (cursorPath + "/cursor.pdf")]; | ||||
NSSize originalSize = [originalImage size]; | NSSize originalSize = [originalImage size]; | ||||
@@ -59,7 +85,7 @@ namespace MouseCursorHelpers | |||||
if (CGImageRef rasterCGImage = [originalImage CGImageForProposedRect: nil | if (CGImageRef rasterCGImage = [originalImage CGImageForProposedRect: nil | ||||
context: nil | context: nil | ||||
hints: [NSDictionary dictionaryWithObjectsAndKeys: | hints: [NSDictionary dictionaryWithObjectsAndKeys: | ||||
NSImageHintCTM, scaleTransform, nil]]) | |||||
NSImageHintCTM, scaleTransform, nil]]) | |||||
{ | { | ||||
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage: rasterCGImage]; | NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage: rasterCGImage]; | ||||
[imageRep setSize: originalSize]; | [imageRep setSize: originalSize]; | ||||
@@ -83,98 +109,88 @@ namespace MouseCursorHelpers | |||||
return fromNSImage (resultImage, NSMakePoint (hotspotX, hotspotY)); | return fromNSImage (resultImage, NSMakePoint (hotspotX, hotspotY)); | ||||
} | } | ||||
} | } | ||||
} | |||||
void* CustomMouseCursorInfo::create() const | |||||
{ | |||||
return MouseCursorHelpers::fromNSImage (imageToNSImage (image, scaleFactor), | |||||
NSMakePoint (hotspot.x, hotspot.y)); | |||||
} | |||||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) | |||||
{ | |||||
JUCE_AUTORELEASEPOOL | |||||
static NSCursor* createCursor (const CustomMouseCursorInfo& info) | |||||
{ | { | ||||
NSCursor* c = nil; | |||||
return fromNSImage (imageToNSImage (info.image, info.scaleFactor), | |||||
NSMakePoint (info.hotspot.x, info.hotspot.y)); | |||||
} | |||||
switch (type) | |||||
static NSCursor* createCursor (const MouseCursor::StandardCursorType type) | |||||
{ | |||||
JUCE_AUTORELEASEPOOL | |||||
{ | { | ||||
case NormalCursor: | |||||
case ParentCursor: c = [NSCursor arrowCursor]; break; | |||||
case NoCursor: return CustomMouseCursorInfo (Image (Image::ARGB, 8, 8, true), {}).create(); | |||||
case DraggingHandCursor: c = [NSCursor openHandCursor]; break; | |||||
case WaitCursor: c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball | |||||
case IBeamCursor: c = [NSCursor IBeamCursor]; break; | |||||
case PointingHandCursor: c = [NSCursor pointingHandCursor]; break; | |||||
case LeftEdgeResizeCursor: c = [NSCursor resizeLeftCursor]; break; | |||||
case RightEdgeResizeCursor: c = [NSCursor resizeRightCursor]; break; | |||||
case CrosshairCursor: c = [NSCursor crosshairCursor]; break; | |||||
case CopyingCursor: | |||||
NSCursor* c = nil; | |||||
switch (type) | |||||
{ | { | ||||
c = [NSCursor dragCopyCursor]; | |||||
break; | |||||
} | |||||
case NormalCursor: | |||||
case ParentCursor: c = [NSCursor arrowCursor]; break; | |||||
case NoCursor: return createCursor ({ Image (Image::ARGB, 8, 8, true), {} }); | |||||
case DraggingHandCursor: c = [NSCursor openHandCursor]; break; | |||||
case WaitCursor: c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball | |||||
case IBeamCursor: c = [NSCursor IBeamCursor]; break; | |||||
case PointingHandCursor: c = [NSCursor pointingHandCursor]; break; | |||||
case LeftEdgeResizeCursor: c = [NSCursor resizeLeftCursor]; break; | |||||
case RightEdgeResizeCursor: c = [NSCursor resizeRightCursor]; break; | |||||
case CrosshairCursor: c = [NSCursor crosshairCursor]; break; | |||||
case CopyingCursor: | |||||
{ | |||||
c = [NSCursor dragCopyCursor]; | |||||
break; | |||||
} | |||||
case UpDownResizeCursor: | |||||
case TopEdgeResizeCursor: | |||||
case BottomEdgeResizeCursor: | |||||
if (void* m = MouseCursorHelpers::fromHIServices ("resizenorthsouth")) | |||||
return m; | |||||
case UpDownResizeCursor: | |||||
case TopEdgeResizeCursor: | |||||
case BottomEdgeResizeCursor: | |||||
if (NSCursor* m = fromHIServices ("resizenorthsouth")) | |||||
return m; | |||||
c = [NSCursor resizeUpDownCursor]; | |||||
break; | |||||
c = [NSCursor resizeUpDownCursor]; | |||||
break; | |||||
case LeftRightResizeCursor: | |||||
if (void* m = MouseCursorHelpers::fromHIServices ("resizeeastwest")) | |||||
return m; | |||||
case LeftRightResizeCursor: | |||||
if (NSCursor* m = fromHIServices ("resizeeastwest")) | |||||
return m; | |||||
c = [NSCursor resizeLeftRightCursor]; | |||||
break; | |||||
c = [NSCursor resizeLeftRightCursor]; | |||||
break; | |||||
case TopLeftCornerResizeCursor: | |||||
case BottomRightCornerResizeCursor: | |||||
return MouseCursorHelpers::fromHIServices ("resizenorthwestsoutheast"); | |||||
case TopLeftCornerResizeCursor: | |||||
case BottomRightCornerResizeCursor: | |||||
return fromHIServices ("resizenorthwestsoutheast"); | |||||
case TopRightCornerResizeCursor: | |||||
case BottomLeftCornerResizeCursor: | |||||
return MouseCursorHelpers::fromHIServices ("resizenortheastsouthwest"); | |||||
case TopRightCornerResizeCursor: | |||||
case BottomLeftCornerResizeCursor: | |||||
return fromHIServices ("resizenortheastsouthwest"); | |||||
case UpDownLeftRightResizeCursor: | |||||
return MouseCursorHelpers::fromHIServices ("move"); | |||||
case UpDownLeftRightResizeCursor: | |||||
return fromHIServices ("move"); | |||||
case NumStandardCursorTypes: | |||||
default: | |||||
jassertfalse; | |||||
break; | |||||
} | |||||
case NumStandardCursorTypes: | |||||
default: | |||||
jassertfalse; | |||||
break; | |||||
} | |||||
[c retain]; | |||||
return c; | |||||
[c retain]; | |||||
return c; | |||||
} | |||||
} | } | ||||
} | |||||
void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool /*isStandard*/) | |||||
{ | |||||
[((NSCursor*) cursorHandle) release]; | |||||
} | |||||
void MouseCursor::showInWindow (ComponentPeer*) const | |||||
{ | |||||
auto c = (NSCursor*) getHandle(); | |||||
if (c == nil) | |||||
c = [NSCursor arrowCursor]; | |||||
[c set]; | |||||
} | |||||
NSCursor* cursorHandle; | |||||
}; | |||||
#else | #else | ||||
void* CustomMouseCursorInfo::create() const { return nullptr; } | |||||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType) { return nullptr; } | |||||
void MouseCursor::deleteMouseCursor (void*, bool) {} | |||||
void MouseCursor::showInWindow (ComponentPeer*) const {} | |||||
class MouseCursor::PlatformSpecificHandle | |||||
{ | |||||
public: | |||||
PlatformSpecificHandle (const MouseCursor::StandardCursorType) {} | |||||
PlatformSpecificHandle (const CustomMouseCursorInfo&) {} | |||||
static void showInWindow (PlatformSpecificHandle*, ComponentPeer*) {} | |||||
}; | |||||
#endif | #endif | ||||
@@ -353,6 +353,7 @@ using SetProcessDPIAwarenessFunc = HRESULT (WINAPI*) | |||||
using SetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (DPI_AWARENESS_CONTEXT); | using SetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (DPI_AWARENESS_CONTEXT); | ||||
using GetDPIForWindowFunc = UINT (WINAPI*) (HWND); | using GetDPIForWindowFunc = UINT (WINAPI*) (HWND); | ||||
using GetDPIForMonitorFunc = HRESULT (WINAPI*) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*); | using GetDPIForMonitorFunc = HRESULT (WINAPI*) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*); | ||||
using GetSystemMetricsForDpiFunc = int (WINAPI*) (int, UINT); | |||||
using GetProcessDPIAwarenessFunc = HRESULT (WINAPI*) (HANDLE, DPI_Awareness*); | using GetProcessDPIAwarenessFunc = HRESULT (WINAPI*) (HANDLE, DPI_Awareness*); | ||||
using GetWindowDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (HWND); | using GetWindowDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (HWND); | ||||
using GetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (); | using GetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (); | ||||
@@ -5134,85 +5135,178 @@ Image juce_createIconForFile (const File& file) | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
void* CustomMouseCursorInfo::create() const | |||||
class MouseCursor::PlatformSpecificHandle | |||||
{ | { | ||||
const int maxW = GetSystemMetrics (SM_CXCURSOR); | |||||
const int maxH = GetSystemMetrics (SM_CYCURSOR); | |||||
public: | |||||
explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type) | |||||
: impl (makeHandle (type)) {} | |||||
Image im (image); | |||||
int hotspotX = hotspot.x; | |||||
int hotspotY = hotspot.y; | |||||
explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info) | |||||
: impl (makeHandle (info)) {} | |||||
if (im.getWidth() > maxW || im.getHeight() > maxH) | |||||
static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer) | |||||
{ | { | ||||
im = im.rescaled (maxW, maxH); | |||||
SetCursor ([&] | |||||
{ | |||||
if (handle != nullptr && handle->impl != nullptr && peer != nullptr) | |||||
return handle->impl->getCursor (*peer); | |||||
hotspotX = (hotspotX * maxW) / juce::jmax (1, image.getWidth()); | |||||
hotspotY = (hotspotY * maxH) / juce::jmax (1, image.getHeight()); | |||||
return LoadCursor (nullptr, IDC_ARROW); | |||||
}()); | |||||
} | } | ||||
return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY); | |||||
} | |||||
private: | |||||
struct Impl | |||||
{ | |||||
virtual ~Impl() = default; | |||||
virtual HCURSOR getCursor (ComponentPeer&) = 0; | |||||
}; | |||||
void MouseCursor::deleteMouseCursor (void* cursorHandle, bool isStandard) | |||||
{ | |||||
if (cursorHandle != nullptr && ! isStandard) | |||||
DestroyCursor ((HCURSOR) cursorHandle); | |||||
} | |||||
class BuiltinImpl : public Impl | |||||
{ | |||||
public: | |||||
explicit BuiltinImpl (HCURSOR cursorIn) | |||||
: cursor (cursorIn) {} | |||||
enum | |||||
{ | |||||
hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor) | |||||
}; | |||||
HCURSOR getCursor (ComponentPeer&) override { return cursor; } | |||||
void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type) | |||||
{ | |||||
LPCTSTR cursorName = IDC_ARROW; | |||||
private: | |||||
HCURSOR cursor; | |||||
}; | |||||
switch (type) | |||||
class ImageImpl : public Impl | |||||
{ | { | ||||
case NormalCursor: | |||||
case ParentCursor: break; | |||||
case NoCursor: return (void*) hiddenMouseCursorHandle; | |||||
case WaitCursor: cursorName = IDC_WAIT; break; | |||||
case IBeamCursor: cursorName = IDC_IBEAM; break; | |||||
case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; | |||||
case CrosshairCursor: cursorName = IDC_CROSS; break; | |||||
public: | |||||
ImageImpl (Image imageIn, Point<int> hotspotIn) | |||||
: image (std::move (imageIn)), hotspot (hotspotIn) {} | |||||
case LeftRightResizeCursor: | |||||
case LeftEdgeResizeCursor: | |||||
case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break; | |||||
~ImageImpl() override | |||||
{ | |||||
for (auto& pair : cursorsBySize) | |||||
DestroyCursor (pair.second); | |||||
} | |||||
HCURSOR getCursor (ComponentPeer& peer) override | |||||
{ | |||||
JUCE_ASSERT_MESSAGE_THREAD; | |||||
case UpDownResizeCursor: | |||||
case TopEdgeResizeCursor: | |||||
case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break; | |||||
static auto getCursorSize = getCursorSizeForPeerFunction(); | |||||
case TopLeftCornerResizeCursor: | |||||
case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break; | |||||
const auto size = getCursorSize (peer); | |||||
const auto iter = cursorsBySize.find (size); | |||||
case TopRightCornerResizeCursor: | |||||
case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break; | |||||
if (iter != cursorsBySize.end()) | |||||
return iter->second; | |||||
const auto imgW = jmax (1, image.getWidth()); | |||||
const auto imgH = jmax (1, image.getHeight()); | |||||
const auto scale = (float) size / (float) unityCursorSize; | |||||
const auto scaleToUse = scale * jmin (1.0f, jmin ((float) unityCursorSize / (float) imgW, | |||||
(float) unityCursorSize / (float) imgH)); | |||||
const auto rescaled = image.rescaled (roundToInt (scaleToUse * (float) imgW), | |||||
roundToInt (scaleToUse * (float) imgH)); | |||||
const auto hx = jlimit (0, rescaled.getWidth(), roundToInt (scaleToUse * (float) hotspot.x)); | |||||
const auto hy = jlimit (0, rescaled.getHeight(), roundToInt (scaleToUse * (float) hotspot.y)); | |||||
return cursorsBySize.emplace (size, IconConverters::createHICONFromImage (rescaled, false, hx, hy)).first->second; | |||||
} | |||||
private: | |||||
Image image; | |||||
Point<int> hotspot; | |||||
std::map<int, HCURSOR> cursorsBySize; | |||||
}; | |||||
static auto getCursorSizeForPeerFunction() -> int (*) (ComponentPeer&) | |||||
{ | |||||
static const auto getDpiForMonitor = []() -> GetDPIForMonitorFunc | |||||
{ | |||||
constexpr auto library = "SHCore.dll"; | |||||
LoadLibraryA (library); | |||||
if (auto* handle = GetModuleHandleA (library)) | |||||
return (GetDPIForMonitorFunc) GetProcAddress (handle, "GetDpiForMonitor"); | |||||
return nullptr; | |||||
}(); | |||||
static const auto getSystemMetricsForDpi = []() -> GetSystemMetricsForDpiFunc | |||||
{ | |||||
constexpr auto library = "User32.dll"; | |||||
LoadLibraryA (library); | |||||
if (auto* handle = GetModuleHandleA (library)) | |||||
return (GetSystemMetricsForDpiFunc) GetProcAddress (handle, "GetSystemMetricsForDpi"); | |||||
return nullptr; | |||||
}(); | |||||
case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break; | |||||
if (getDpiForMonitor == nullptr || getSystemMetricsForDpi == nullptr) | |||||
return [] (ComponentPeer&) { return unityCursorSize; }; | |||||
case DraggingHandCursor: | |||||
return [] (ComponentPeer& p) | |||||
{ | { | ||||
static void* dragHandCursor = [] | |||||
const ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { p.getNativeHandle() }; | |||||
UINT dpiX = 0, dpiY = 0; | |||||
if (auto* monitor = MonitorFromWindow ((HWND) p.getNativeHandle(), MONITOR_DEFAULTTONULL)) | |||||
if (SUCCEEDED (getDpiForMonitor (monitor, MDT_Default, &dpiX, &dpiY))) | |||||
return getSystemMetricsForDpi (SM_CXCURSOR, dpiX); | |||||
return unityCursorSize; | |||||
}; | |||||
} | |||||
static constexpr auto unityCursorSize = 32; | |||||
static std::unique_ptr<Impl> makeHandle (const CustomMouseCursorInfo& info) | |||||
{ | |||||
return std::make_unique<ImageImpl> (info.image, info.hotspot); | |||||
} | |||||
static std::unique_ptr<Impl> makeHandle (const MouseCursor::StandardCursorType type) | |||||
{ | |||||
LPCTSTR cursorName = IDC_ARROW; | |||||
switch (type) | |||||
{ | |||||
case NormalCursor: | |||||
case ParentCursor: break; | |||||
case NoCursor: return std::make_unique<BuiltinImpl> (nullptr); | |||||
case WaitCursor: cursorName = IDC_WAIT; break; | |||||
case IBeamCursor: cursorName = IDC_IBEAM; break; | |||||
case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; | |||||
case CrosshairCursor: cursorName = IDC_CROSS; break; | |||||
case LeftRightResizeCursor: | |||||
case LeftEdgeResizeCursor: | |||||
case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break; | |||||
case UpDownResizeCursor: | |||||
case TopEdgeResizeCursor: | |||||
case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break; | |||||
case TopLeftCornerResizeCursor: | |||||
case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break; | |||||
case TopRightCornerResizeCursor: | |||||
case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break; | |||||
case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break; | |||||
case DraggingHandCursor: | |||||
{ | { | ||||
static const unsigned char dragHandData[] | static const unsigned char dragHandData[] | ||||
{ 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, | { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, | ||||
16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138, | 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138, | ||||
98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; | 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; | ||||
return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), { 8, 7 }).create(); | |||||
}(); | |||||
return dragHandCursor; | |||||
} | |||||
return makeHandle ({ ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), { 8, 7 } }); | |||||
} | |||||
case CopyingCursor: | |||||
{ | |||||
static void* copyCursor = [] | |||||
case CopyingCursor: | |||||
{ | { | ||||
static const unsigned char copyCursorData[] | static const unsigned char copyCursorData[] | ||||
{ 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, | { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, | ||||
@@ -5220,36 +5314,27 @@ void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorT | |||||
12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83, | 12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83, | ||||
5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 }; | 5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 }; | ||||
return CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, sizeof (copyCursorData)), { 1, 3 }).create(); | |||||
}(); | |||||
return makeHandle ({ ImageFileFormat::loadFrom (copyCursorData, sizeof (copyCursorData)), { 1, 3 } }); | |||||
} | |||||
return copyCursor; | |||||
case NumStandardCursorTypes: JUCE_FALLTHROUGH | |||||
default: | |||||
jassertfalse; break; | |||||
} | } | ||||
case NumStandardCursorTypes: JUCE_FALLTHROUGH | |||||
default: | |||||
jassertfalse; break; | |||||
} | |||||
return std::make_unique<BuiltinImpl> ([&] | |||||
{ | |||||
if (auto* c = LoadCursor (nullptr, cursorName)) | |||||
return c; | |||||
if (auto cursorH = LoadCursor (nullptr, cursorName)) | |||||
return cursorH; | |||||
return LoadCursor (nullptr, IDC_ARROW); | |||||
}()); | |||||
} | |||||
return LoadCursor (nullptr, IDC_ARROW); | |||||
} | |||||
std::unique_ptr<Impl> impl; | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
void MouseCursor::showInWindow (ComponentPeer*) const | |||||
{ | |||||
auto c = (HCURSOR) getHandle(); | |||||
if (c == nullptr) | |||||
c = LoadCursor (nullptr, IDC_ARROW); | |||||
else if (c == (HCURSOR) hiddenMouseCursorHandle) | |||||
c = nullptr; | |||||
SetCursor (c); | |||||
} | |||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||||
} // namespace juce | } // namespace juce |
@@ -26,8 +26,8 @@ | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
extern void* createDraggingHandCursor(); | |||||
extern ComponentPeer* getPeerFor (::Window); | |||||
static Cursor createDraggingHandCursor(); | |||||
ComponentPeer* getPeerFor (::Window); | |||||
//============================================================================== | //============================================================================== | ||||
class X11DragState | class X11DragState | ||||
@@ -2193,10 +2193,10 @@ void XWindowSystem::setMousePosition (Point<float> pos) const | |||||
roundToInt (pos.getX()), roundToInt (pos.getY())); | roundToInt (pos.getX()), roundToInt (pos.getY())); | ||||
} | } | ||||
void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point<int> hotspot) const | |||||
Cursor XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point<int> hotspot) const | |||||
{ | { | ||||
if (display == nullptr) | if (display == nullptr) | ||||
return nullptr; | |||||
return {}; | |||||
XWindowSystemUtilities::ScopedXLock xLock; | XWindowSystemUtilities::ScopedXLock xLock; | ||||
@@ -2217,9 +2217,9 @@ void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point<int> | |||||
for (int x = 0; x < (int) imageW; ++x) | for (int x = 0; x < (int) imageW; ++x) | ||||
*dest++ = image.getPixelAt (x, y).getARGB(); | *dest++ = image.getPixelAt (x, y).getARGB(); | ||||
auto* result = (void*) X11Symbols::getInstance()->xcursorImageLoadCursor (display, xcImage.get()); | |||||
auto result = X11Symbols::getInstance()->xcursorImageLoadCursor (display, xcImage.get()); | |||||
if (result != nullptr) | |||||
if (result != Cursor{}) | |||||
return result; | return result; | ||||
} | } | ||||
#endif | #endif | ||||
@@ -2229,7 +2229,7 @@ void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point<int> | |||||
unsigned int cursorW, cursorH; | unsigned int cursorW, cursorH; | ||||
if (! X11Symbols::getInstance()->xQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH)) | if (! X11Symbols::getInstance()->xQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH)) | ||||
return nullptr; | |||||
return {}; | |||||
Image im (Image::ARGB, (int) cursorW, (int) cursorH, true); | Image im (Image::ARGB, (int) cursorW, (int) cursorH, true); | ||||
@@ -2279,20 +2279,20 @@ void* XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point<int> | |||||
black.red = black.green = black.blue = 0; | black.red = black.green = black.blue = 0; | ||||
white.red = white.green = white.blue = 0xffff; | white.red = white.green = white.blue = 0xffff; | ||||
return (void*) X11Symbols::getInstance()->xCreatePixmapCursor (display, sourcePixmap.value, maskPixmap.value, &white, &black, | |||||
(unsigned int) hotspotX, (unsigned int) hotspotY); | |||||
return X11Symbols::getInstance()->xCreatePixmapCursor (display, sourcePixmap.value, maskPixmap.value, &white, &black, | |||||
(unsigned int) hotspotX, (unsigned int) hotspotY); | |||||
} | } | ||||
void XWindowSystem::deleteMouseCursor (void* cursorHandle) const | |||||
void XWindowSystem::deleteMouseCursor (Cursor cursorHandle) const | |||||
{ | { | ||||
if (cursorHandle != nullptr && display != nullptr) | |||||
if (cursorHandle != Cursor{} && display != nullptr) | |||||
{ | { | ||||
XWindowSystemUtilities::ScopedXLock xLock; | XWindowSystemUtilities::ScopedXLock xLock; | ||||
X11Symbols::getInstance()->xFreeCursor (display, (Cursor) cursorHandle); | X11Symbols::getInstance()->xFreeCursor (display, (Cursor) cursorHandle); | ||||
} | } | ||||
} | } | ||||
void* createDraggingHandCursor() | |||||
static Cursor createDraggingHandCursor() | |||||
{ | { | ||||
constexpr unsigned char dragHandData[] = { | constexpr unsigned char dragHandData[] = { | ||||
71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,16,0, | 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,16,0, | ||||
@@ -2300,10 +2300,11 @@ void* createDraggingHandCursor() | |||||
114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 | 114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 | ||||
}; | }; | ||||
return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, (size_t) numElementsInArray (dragHandData)), { 8, 7 }).create(); | |||||
auto image = ImageFileFormat::loadFrom (dragHandData, (size_t) numElementsInArray (dragHandData)); | |||||
return XWindowSystem::getInstance()->createCustomMouseCursorInfo (std::move (image), { 8, 7 }); | |||||
} | } | ||||
void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType type) const | |||||
Cursor XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType type) const | |||||
{ | { | ||||
if (display == nullptr) | if (display == nullptr) | ||||
return None; | return None; | ||||
@@ -2314,7 +2315,7 @@ void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType | |||||
{ | { | ||||
case MouseCursor::NormalCursor: | case MouseCursor::NormalCursor: | ||||
case MouseCursor::ParentCursor: return None; // Use parent cursor | case MouseCursor::ParentCursor: return None; // Use parent cursor | ||||
case MouseCursor::NoCursor: return CustomMouseCursorInfo (Image (Image::ARGB, 16, 16, true), {}).create(); | |||||
case MouseCursor::NoCursor: return XWindowSystem::createCustomMouseCursorInfo (Image (Image::ARGB, 16, 16, true), {}); | |||||
case MouseCursor::WaitCursor: shape = XC_watch; break; | case MouseCursor::WaitCursor: shape = XC_watch; break; | ||||
case MouseCursor::IBeamCursor: shape = XC_xterm; break; | case MouseCursor::IBeamCursor: shape = XC_xterm; break; | ||||
@@ -2342,7 +2343,8 @@ void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType | |||||
252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 | 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 | ||||
}; | }; | ||||
return CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, (size_t) numElementsInArray (copyCursorData)), { 1, 3 }).create(); | |||||
auto image = ImageFileFormat::loadFrom (copyCursorData, (size_t) numElementsInArray (copyCursorData)); | |||||
return createCustomMouseCursorInfo (std::move (image), { 1, 3 }); | |||||
} | } | ||||
case MouseCursor::NumStandardCursorTypes: | case MouseCursor::NumStandardCursorTypes: | ||||
@@ -2355,10 +2357,10 @@ void* XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType | |||||
XWindowSystemUtilities::ScopedXLock xLock; | XWindowSystemUtilities::ScopedXLock xLock; | ||||
return (void*) X11Symbols::getInstance()->xCreateFontCursor (display, shape); | |||||
return X11Symbols::getInstance()->xCreateFontCursor (display, shape); | |||||
} | } | ||||
void XWindowSystem::showCursor (::Window windowH, void* cursorHandle) const | |||||
void XWindowSystem::showCursor (::Window windowH, Cursor cursorHandle) const | |||||
{ | { | ||||
jassert (windowH != 0); | jassert (windowH != 0); | ||||
@@ -213,10 +213,10 @@ public: | |||||
Point<float> getCurrentMousePosition() const; | Point<float> getCurrentMousePosition() const; | ||||
void setMousePosition (Point<float> pos) const; | void setMousePosition (Point<float> pos) const; | ||||
void* createCustomMouseCursorInfo (const Image&, Point<int> hotspot) const; | |||||
void deleteMouseCursor (void* cursorHandle) const; | |||||
void* createStandardMouseCursor (MouseCursor::StandardCursorType) const; | |||||
void showCursor (::Window, void* cursorHandle) const; | |||||
Cursor createCustomMouseCursorInfo (const Image&, Point<int> hotspot) const; | |||||
void deleteMouseCursor (Cursor cursorHandle) const; | |||||
Cursor createStandardMouseCursor (MouseCursor::StandardCursorType) const; | |||||
void showCursor (::Window, Cursor cursorHandle) const; | |||||
bool isKeyCurrentlyDown (int keyCode) const; | bool isKeyCurrentlyDown (int keyCode) const; | ||||
ModifierKeys getNativeRealtimeModifiers() const; | ModifierKeys getNativeRealtimeModifiers() const; | ||||