| @@ -929,8 +929,11 @@ CGContextRef juce_getImageContext (const Image& image) | |||||
| #endif | #endif | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| NSImage* imageToNSImage (const Image& image, float scaleFactor) | |||||
| NSImage* imageToNSImage (const ScaledImage& scaled) | |||||
| { | { | ||||
| const auto image = scaled.getImage(); | |||||
| const auto scaleFactor = scaled.getScale(); | |||||
| JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
| { | { | ||||
| NSImage* im = [[NSImage alloc] init]; | NSImage* im = [[NSImage alloc] init]; | ||||
| @@ -102,7 +102,7 @@ CGContextRef juce_getImageContext (const Image&); | |||||
| #endif | #endif | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| NSImage* imageToNSImage (const Image& image, float scaleFactor = 1.0f); | |||||
| NSImage* imageToNSImage (const ScaledImage& image); | |||||
| #endif | #endif | ||||
| } // namespace juce | } // namespace juce | ||||
| @@ -127,15 +127,8 @@ namespace juce | |||||
| struct CustomMouseCursorInfo | struct CustomMouseCursorInfo | ||||
| { | { | ||||
| CustomMouseCursorInfo() = default; | |||||
| CustomMouseCursorInfo (const Image& im, Point<int> hs, float scale = 1.0f) noexcept | |||||
| : image (im), hotspot (hs), scaleFactor (scale) | |||||
| {} | |||||
| Image image; | |||||
| ScaledImage image; | |||||
| Point<int> hotspot; | Point<int> hotspot; | ||||
| float scaleFactor = 1.0f; | |||||
| }; | }; | ||||
| } // namespace juce | } // namespace juce | ||||
| @@ -36,14 +36,14 @@ public: | |||||
| { | { | ||||
| } | } | ||||
| SharedCursorHandle (const Image& image, Point<int> hotSpot, float scaleFactor) | |||||
| : info { image, hotSpot, scaleFactor }, | |||||
| SharedCursorHandle (const ScaledImage& image, Point<int> hotSpot) | |||||
| : info { image, hotSpot }, | |||||
| handle (info), | handle (info), | ||||
| standardType (MouseCursor::NormalCursor), | standardType (MouseCursor::NormalCursor), | ||||
| standard (false) | 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.getImage().getBounds().contains (hotSpot)); | |||||
| } | } | ||||
| static std::shared_ptr<SharedCursorHandle> createStandard (const MouseCursor::StandardCursorType type) | static std::shared_ptr<SharedCursorHandle> createStandard (const MouseCursor::StandardCursorType type) | ||||
| @@ -92,12 +92,17 @@ MouseCursor::MouseCursor (const StandardCursorType type) | |||||
| } | } | ||||
| MouseCursor::MouseCursor (const Image& image, int hotSpotX, int hotSpotY) | MouseCursor::MouseCursor (const Image& image, int hotSpotX, int hotSpotY) | ||||
| : MouseCursor (image, hotSpotX, hotSpotY, 1.0f) | |||||
| : MouseCursor (ScaledImage (image), { hotSpotX, hotSpotY }) | |||||
| { | { | ||||
| } | } | ||||
| MouseCursor::MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor) | MouseCursor::MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor) | ||||
| : cursorHandle (std::make_shared<SharedCursorHandle> (image, Point<int> { hotSpotX, hotSpotY }, scaleFactor)) | |||||
| : MouseCursor (ScaledImage (image, scaleFactor), { hotSpotX, hotSpotY }) | |||||
| { | |||||
| } | |||||
| MouseCursor::MouseCursor (const ScaledImage& image, Point<int> hotSpot) | |||||
| : cursorHandle (std::make_shared<SharedCursorHandle> (image, hotSpot)) | |||||
| { | { | ||||
| } | } | ||||
| @@ -103,6 +103,16 @@ public: | |||||
| */ | */ | ||||
| MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor); | MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor); | ||||
| /** Creates a custom cursor from an image. | |||||
| @param image the image to use for the cursor - if this is bigger than the | |||||
| system can manage, it might get scaled down first, and might | |||||
| also have to be turned to black-and-white if it can't do colour | |||||
| cursors. | |||||
| @param hotSpot the position of the cursor's hotspot within the image | |||||
| */ | |||||
| MouseCursor (const ScaledImage& image, Point<int> hotSpot); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a copy of another cursor object. */ | /** Creates a copy of another cursor object. */ | ||||
| MouseCursor (const MouseCursor&); | MouseCursor (const MouseCursor&); | ||||
| @@ -674,7 +674,9 @@ public: | |||||
| private: | private: | ||||
| static Cursor makeHandle (const CustomMouseCursorInfo& info) | static Cursor makeHandle (const CustomMouseCursorInfo& info) | ||||
| { | { | ||||
| return XWindowSystem::getInstance()->createCustomMouseCursorInfo (info.image, info.hotspot); | |||||
| const auto image = info.image.getImage(); | |||||
| return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image.rescaled ((int) (image.getWidth() / info.image.getScale()), | |||||
| (int) (image.getHeight() / info.image.getScale())), info.hotspot); | |||||
| } | } | ||||
| static Cursor makeHandle (MouseCursor::StandardCursorType type) | static Cursor makeHandle (MouseCursor::StandardCursorType type) | ||||
| @@ -111,7 +111,7 @@ private: | |||||
| } | } | ||||
| static NSCursor* createCursor (const CustomMouseCursorInfo& info) | static NSCursor* createCursor (const CustomMouseCursorInfo& info) | ||||
| { | { | ||||
| return fromNSImage (imageToNSImage (info.image, info.scaleFactor), | |||||
| return fromNSImage (imageToNSImage (info.image), | |||||
| NSMakePoint (info.hotspot.x, info.hotspot.y)); | NSMakePoint (info.hotspot.x, info.hotspot.y)); | ||||
| } | } | ||||
| @@ -125,7 +125,7 @@ private: | |||||
| { | { | ||||
| case NormalCursor: | case NormalCursor: | ||||
| case ParentCursor: c = [NSCursor arrowCursor]; break; | case ParentCursor: c = [NSCursor arrowCursor]; break; | ||||
| case NoCursor: return createCursor ({ Image (Image::ARGB, 8, 8, true), {} }); | |||||
| case NoCursor: return createCursor ({ ScaledImage (Image (Image::ARGB, 8, 8, true)), {} }); | |||||
| case DraggingHandCursor: c = [NSCursor openHandCursor]; break; | case DraggingHandCursor: c = [NSCursor openHandCursor]; break; | ||||
| case WaitCursor: c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball | case WaitCursor: c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball | ||||
| case IBeamCursor: c = [NSCursor IBeamCursor]; break; | case IBeamCursor: c = [NSCursor IBeamCursor]; break; | ||||
| @@ -537,7 +537,7 @@ public: | |||||
| if (! windowRepresentsFile) | if (! windowRepresentsFile) | ||||
| [window setRepresentedFilename:juceStringToNS (" ")]; // can't just use an empty string for some reason... | [window setRepresentedFilename:juceStringToNS (" ")]; // can't just use an empty string for some reason... | ||||
| [[window standardWindowButton:NSWindowDocumentIconButton] setImage:imageToNSImage (newIcon)]; | |||||
| [[window standardWindowButton:NSWindowDocumentIconButton] setImage:imageToNSImage (ScaledImage (newIcon))]; | |||||
| } | } | ||||
| } | } | ||||
| @@ -5197,14 +5197,15 @@ private: | |||||
| if (iter != cursorsBySize.end()) | if (iter != cursorsBySize.end()) | ||||
| return iter->second; | return iter->second; | ||||
| const auto imgW = jmax (1, info.image.getWidth()); | |||||
| const auto imgH = jmax (1, info.image.getHeight()); | |||||
| const auto img = info.image.getImage(); | |||||
| const auto imgW = jmax (1, img.getWidth()); | |||||
| const auto imgH = jmax (1, img.getHeight()); | |||||
| const auto scale = (float) size / (float) unityCursorSize; | const auto scale = (float) size / (float) unityCursorSize; | ||||
| const auto scaleToUse = scale * jmin (1.0f, jmin ((float) unityCursorSize / (float) imgW, | const auto scaleToUse = scale * jmin (1.0f, jmin ((float) unityCursorSize / (float) imgW, | ||||
| (float) unityCursorSize / (float) imgH)) / info.scaleFactor; | |||||
| const auto rescaled = info.image.rescaled (roundToInt (scaleToUse * (float) imgW), | |||||
| roundToInt (scaleToUse * (float) imgH)); | |||||
| (float) unityCursorSize / (float) imgH)) / info.image.getScale(); | |||||
| const auto rescaled = img.rescaled (roundToInt (scaleToUse * (float) imgW), | |||||
| roundToInt (scaleToUse * (float) imgH)); | |||||
| const auto hx = jlimit (0, rescaled.getWidth(), roundToInt (scaleToUse * (float) info.hotspot.x)); | const auto hx = jlimit (0, rescaled.getWidth(), roundToInt (scaleToUse * (float) info.hotspot.x)); | ||||
| const auto hy = jlimit (0, rescaled.getHeight(), roundToInt (scaleToUse * (float) info.hotspot.y)); | const auto hy = jlimit (0, rescaled.getHeight(), roundToInt (scaleToUse * (float) info.hotspot.y)); | ||||
| @@ -5301,7 +5302,7 @@ private: | |||||
| 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 makeHandle ({ ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), { 8, 7 } }); | |||||
| return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData))), { 8, 7 } }); | |||||
| } | } | ||||
| case CopyingCursor: | case CopyingCursor: | ||||
| @@ -5312,7 +5313,7 @@ private: | |||||
| 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 makeHandle ({ ImageFileFormat::loadFrom (copyCursorData, sizeof (copyCursorData)), { 1, 3 } }); | |||||
| return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (copyCursorData, sizeof (copyCursorData))), { 1, 3 } }); | |||||
| } | } | ||||
| case NumStandardCursorTypes: JUCE_FALLTHROUGH | case NumStandardCursorTypes: JUCE_FALLTHROUGH | ||||
| @@ -36,7 +36,7 @@ struct StatusItemContainer : public Timer | |||||
| { | { | ||||
| //============================================================================== | //============================================================================== | ||||
| StatusItemContainer (SystemTrayIconComponent& iconComp, const Image& im) | StatusItemContainer (SystemTrayIconComponent& iconComp, const Image& im) | ||||
| : owner (iconComp), statusIcon (imageToNSImage (im)) | |||||
| : owner (iconComp), statusIcon (imageToNSImage (ScaledImage (im))) | |||||
| { | { | ||||
| } | } | ||||
| @@ -51,7 +51,7 @@ struct StatusItemContainer : public Timer | |||||
| void updateIcon (const Image& newImage) | void updateIcon (const Image& newImage) | ||||
| { | { | ||||
| statusIcon.reset (imageToNSImage (newImage)); | |||||
| statusIcon.reset (imageToNSImage (ScaledImage (newImage))); | |||||
| setIconSize(); | setIconSize(); | ||||
| configureIcon(); | configureIcon(); | ||||
| } | } | ||||