From e7b75a725d981861608c5b5c310e9d632c54998a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 26 Mar 2024 16:03:14 -0300 Subject: [PATCH 01/41] [win] Add move assignment operator and a new contructor that receives a pointer to the COM object. --- base/win/comptr.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/base/win/comptr.h b/base/win/comptr.h index 9ca3c11c8..eb3aba504 100644 --- a/base/win/comptr.h +++ b/base/win/comptr.h @@ -31,6 +31,11 @@ namespace base { std::swap(m_ptr, tmp.m_ptr); } + explicit ComPtr(T* p) : m_ptr(p) { + if (m_ptr) + m_ptr->AddRef(); + } + ~ComPtr() { reset(); } @@ -41,11 +46,18 @@ namespace base { // Add new reference using operator=() ComPtr& operator=(const ComPtr& p) { - if (m_ptr) - m_ptr->Release(); - m_ptr = p.m_ptr; - if (m_ptr) - m_ptr->AddRef(); + if (this != &p) { + if (m_ptr) + m_ptr->Release(); + m_ptr = p.m_ptr; + if (m_ptr) + m_ptr->AddRef(); + } + return *this; + } + + ComPtr& operator=(ComPtr&& p) noexcept { + std::swap(m_ptr, p.m_ptr); return *this; } From 219fc625e2dcc7515b54fbc3470f3804fb29b5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Wed, 10 Apr 2024 18:03:33 -0300 Subject: [PATCH 02/41] Define drag and drop API --- os/dnd.h | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ os/os.h | 1 + os/window.cpp | 44 ++++++++++++++++++++ os/window.h | 14 +++++++ 4 files changed, 167 insertions(+) create mode 100644 os/dnd.h diff --git a/os/dnd.h b/os/dnd.h new file mode 100644 index 000000000..d43fee950 --- /dev/null +++ b/os/dnd.h @@ -0,0 +1,108 @@ +// LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef OS_DND_H_INCLUDED +#define OS_DND_H_INCLUDED +#pragma once + +#include "base/paths.h" +#include "base/debug.h" +#include "gfx/point.h" +#include "os/surface.h" + +#include + +#pragma push_macro("None") +#undef None // Undefine the X11 None macro + +namespace os { + + class Window; + + // Operations that can be supported by source and target windows in a drag + // and drop operation. + enum DropOperation { + None = 0, + Copy = 1, + Move = 2, + Link = 4, + Any = Copy | Move | Link, + }; + + // Types of representations supported for each DragDataItem. + enum class DragDataItemType { + Paths, + Image, + }; + + // Interface to get dragged data from the platform's implementation. + class DragDataProvider { + public: + virtual ~DragDataProvider() {} + virtual base::paths getPaths() = 0; + virtual SurfaceRef getImage() = 0; + virtual bool contains(DragDataItemType type) { return false; } + }; + + class DragEvent { + public: + DragEvent(os::Window* target, + DropOperation supportedOperations, + const gfx::Point& dragPosition, + DragDataProvider* dataProvider) + : m_target(target) + , m_supportedOperations(supportedOperations) + , m_position(dragPosition) + , m_dataProvider(dataProvider) {} + + // Destination window of the DragEvent. + os::Window* target() const { return m_target; } + DropOperation dropResult() const { return m_dropResult; } + DropOperation supportedOperations() const { return m_supportedOperations; } + bool acceptDrop() const { return m_acceptDrop; } + const gfx::Point& position() { return m_position; } + DragDataProvider* dataProvider() { return m_dataProvider; } + + // Sets what will be the outcome of dropping the dragged data when it is + // accepted by the target window. Only one of the enum values should be passed, + // do not combine values using bitwise operators. + void dropResult(DropOperation operation) { m_dropResult = operation; } + // Set this to true when the dropped data was accepted/processed by the + // target window, or set to false otherwise. + void acceptDrop(bool value) { m_acceptDrop = value; } + + bool sourceSupports(DropOperation op) { return (m_supportedOperations & op) == op; } + + private: + os::Window* m_target; + DropOperation m_dropResult = DropOperation::Copy; + // Bitwise OR of the operations supported by the drag and drop source. + DropOperation m_supportedOperations; + bool m_acceptDrop = false; + gfx::Point m_position; + DragDataProvider* m_dataProvider; + }; + + class DragTarget { + public: + virtual ~DragTarget() {}; + + // Called when a drag action enters a window that supports DnD. The + // DragEvent::dropResult must be set to the operation that is expected + // to occur by the target window once the drop is accepted. + virtual void dragEnter(os::DragEvent& ev) {} + // Called when the dragged data exits the window that supports DnD. + virtual void dragLeave(os::DragEvent& ev) {} + virtual void drag(os::DragEvent& ev) {} + virtual void drop(os::DragEvent& ev) {} + }; + + +} // namespace os + +#pragma pop_macro("None") + +#endif \ No newline at end of file diff --git a/os/os.h b/os/os.h index fa8f237ef..ea7f7acd2 100644 --- a/os/os.h +++ b/os/os.h @@ -16,6 +16,7 @@ #include "gfx/region.h" #include "gfx/size.h" #include "os/capabilities.h" +#include "os/dnd.h" #include "os/draw_text.h" #include "os/error.h" #include "os/event.h" diff --git a/os/window.cpp b/os/window.cpp index 9b11fc15e..d84a704c6 100644 --- a/os/window.cpp +++ b/os/window.cpp @@ -48,6 +48,50 @@ void Window::queueEvent(os::Event& ev) onQueueEvent(ev); } +void Window::notifyDragEnter(os::DragEvent& ev) +{ + onDragEnter(ev); +} + +void Window::notifyDrag(os::DragEvent& ev) +{ + onDrag(ev); +} + +void Window::notifyDragLeave(os::DragEvent& ev) +{ + onDragLeave(ev); +} + +void Window::notifyDrop(os::DragEvent& ev) +{ + onDrop(ev); +} + +void Window::onDragEnter(os::DragEvent& ev) +{ + if (m_dragTarget) + m_dragTarget->dragEnter(ev); +} + +void Window::onDrag(os::DragEvent& ev) +{ + if (m_dragTarget) + m_dragTarget->drag(ev); +} + +void Window::onDragLeave(os::DragEvent& ev) +{ + if (m_dragTarget) + m_dragTarget->dragLeave(ev); +} + +void Window::onDrop(os::DragEvent& ev) +{ + if (m_dragTarget) + m_dragTarget->drop(ev); +} + void Window::onQueueEvent(Event& ev) { // Some events are used more than one time (e.g. to send MouseEnter diff --git a/os/window.h b/os/window.h index b1ff878c6..829f63ba2 100644 --- a/os/window.h +++ b/os/window.h @@ -12,6 +12,7 @@ #include "gfx/point.h" #include "os/color_space.h" #include "os/cursor.h" +#include "os/dnd.h" #include "os/native_cursor.h" #include "os/ref.h" #include "os/screen.h" @@ -194,6 +195,9 @@ namespace os { // devices like stylus can be used to move and resize the window. std::function handleHitTest = nullptr; + void setDragTarget(DragTarget* delegate) { m_dragTarget = delegate; } + bool hasDragTarget() { return m_dragTarget != nullptr; } + template T* userData() { return reinterpret_cast(m_userData); } @@ -204,11 +208,21 @@ namespace os { virtual GrDirectContext* sk_grCtx() const = 0; #endif + void notifyDragEnter(os::DragEvent& ev); + void notifyDrag(os::DragEvent& ev); + void notifyDragLeave(os::DragEvent& ev); + void notifyDrop(os::DragEvent& ev); + protected: virtual void onQueueEvent(Event& ev); + virtual void onDragEnter(os::DragEvent& ev); + virtual void onDrag(os::DragEvent& ev); + virtual void onDragLeave(os::DragEvent& ev); + virtual void onDrop(os::DragEvent& ev); private: void* m_userData; + DragTarget* m_dragTarget = nullptr; }; } // namespace os From 4c9548179ddc1b8640eeb5d6f865968f820c9569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Thu, 11 Apr 2024 10:26:03 -0300 Subject: [PATCH 03/41] Add drag and drop example --- examples/CMakeLists.txt | 1 + examples/drag_and_drop.cpp | 208 +++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 examples/drag_and_drop.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 728db7a02..4d97155be 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,6 +19,7 @@ if(LAF_BACKEND STREQUAL "skia") laf_add_example(base64 CONSOLE) laf_add_example(complextextlayout GUI) laf_add_example(custom_window GUI) + laf_add_example(drag_and_drop GUI) laf_add_example(floating_window GUI) laf_add_example(hello_laf GUI) laf_add_example(listfonts CONSOLE) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp new file mode 100644 index 000000000..0e140e7c2 --- /dev/null +++ b/examples/drag_and_drop.cpp @@ -0,0 +1,208 @@ +// LAF Library +// Copyright (c) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + + +#include "base/paths.h" +#include "gfx/hsv.h" +#include "gfx/point.h" +#include "gfx/rect.h" +#include "gfx/rgb.h" +#include "os/dnd.h" +#include "os/draw_text.h" +#include "os/os.h" +#include "os/paint.h" +#include "os/surface.h" + +#include +#include +#include +#include +#include +#include + +using Boxes = std::vector; + +struct WindowData { + bool dragEnter; + bool dragLeave; + int drag; + base::paths paths; + os::SurfaceRef image; + gfx::Point dragPosition; + gfx::Rect dropZone; +}; + +static WindowData windowData; + +static void redraw_window(os::Window* window); + +class DragTarget : public os::DragTarget { +public: + void dragEnter(os::DragEvent& ev) override { + if (!windowData.dropZone.contains(ev.position()) || !ev.sourceSupports(os::DropOperation::Copy)) + ev.dropResult(os::DropOperation::None); + else if (ev.sourceSupports(os::DropOperation::Copy)) + ev.dropResult(os::DropOperation::Copy); + + windowData.dragEnter = true; + windowData.dragLeave = false; + windowData.drag = 0; + windowData.dragPosition = ev.position(); + redraw_window(ev.target()); + ev.target()->invalidate(); + } + void dragLeave(os::DragEvent& ev) override { + windowData.dragEnter = false; + windowData.dragLeave = true; + windowData.dragPosition = ev.position(); + redraw_window(ev.target()); + ev.target()->invalidate(); + } + void drag(os::DragEvent& ev) override { + ++windowData.drag; + windowData.dragPosition = ev.position(); + redraw_window(ev.target()); + ev.target()->invalidate(); + } + void drop(os::DragEvent& ev) override { + windowData.dragEnter = false; + windowData.dragLeave = false; + windowData.dragPosition = {0, 0}; + ev.acceptDrop(windowData.dropZone.contains(ev.position())); + + if (ev.acceptDrop()) { + if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) + windowData.paths = ev.dataProvider()->getPaths(); + if (ev.dataProvider()->contains(os::DragDataItemType::Image)) + windowData.image = ev.dataProvider()->getImage(); + } + + redraw_window(ev.target()); + ev.target()->invalidate(); + } +}; + +static void redraw_window(os::Window* window) +{ + os::Surface* s = window->surface(); + os::Paint paint; + paint.color(gfx::rgba(0, 0, 0)); + s->drawRect(window->bounds(), paint); + + paint.color(gfx::rgba(255, 255, 255)); + + char buf[256]; + int y = 12; + std::snprintf(buf, sizeof(buf), "Drag Position = [%d, %d]", windowData.dragPosition.x, windowData.dragPosition.y); + os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); + y += 12; + std::snprintf(buf, sizeof(buf), "Drag Enter = %s", windowData.dragEnter ? "true" : "false"); + os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); + y += 12; + std::snprintf(buf, sizeof(buf), "Drag = %d", windowData.drag); + os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); + y += 12; + std::snprintf(buf, sizeof(buf), "Drag Leave = %s", windowData.dragLeave ? "true" : "false"); + os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); + + if (!windowData.paths.empty()) { + y += 12; + std::snprintf(buf, sizeof(buf), "Paths = %lu", windowData.paths.size()); + os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); + for (const auto& path : windowData.paths) { + y += 12; + std::snprintf(buf, sizeof(buf), "%s", path.c_str()); + os::draw_text(s, nullptr, buf, gfx::Point(12, y), &paint); + } + } + + if (windowData.image) { + y += 12; + s->drawSurface(windowData.image.get(), 0, y); + } + + paint.style(os::Paint::Style::Stroke); + s->drawRect(window->bounds(), paint); + + + auto zoneColor = gfx::rgba(100, 255, 100); + auto textColor = zoneColor; + if (windowData.dropZone.contains(windowData.dragPosition)){ + paint.style(os::Paint::Style::Fill); + paint.color(zoneColor); + s->drawRect(windowData.dropZone, paint); + textColor = gfx::rgba(0, 0, 0); + } + + paint.color(zoneColor); + paint.style(os::Paint::Style::Stroke); + s->drawRect(windowData.dropZone, paint); + + paint.color(textColor); + paint.style(os::Paint::Style::Fill); + os::draw_text(s, nullptr, "Drop here!", windowData.dropZone.center(), &paint, os::TextAlign::Center); +} + +static os::WindowRef create_window(const std::string& title, + const os::WindowSpec& spec, + os::DragTarget& dragTarget) +{ + os::WindowRef newWindow = os::instance()->makeWindow(spec); + newWindow->setCursor(os::NativeCursor::Arrow); + newWindow->setTitle(title); + newWindow->setVisible(true); + newWindow->setDragTarget(&dragTarget); + windowData.dropZone = {32, spec.frame().h - 64 - 40, 64, 64}; + redraw_window(newWindow.get()); + return newWindow; +} + +int app_main(int argc, char* argv[]) +{ + auto system = os::make_system(); + system->setAppMode(os::AppMode::GUI); + system->handleWindowResize = redraw_window; + + auto screen = system->mainScreen(); + os::WindowSpec spec; + DragTarget dragTarget; + spec.titled(true); + spec.position(os::WindowSpec::Position::Frame); + auto frame = screen->workarea()/2; + spec.frame(frame); + spec.screen(screen); + os::WindowRef window = create_window("Drag & Drop example", spec, dragTarget); + + bool running = true; + + system->finishLaunching(); + system->activateApp(); + + os::EventQueue* queue = system->eventQueue(); + os::Event ev; + while (running) { + queue->getEvent(ev); + + switch (ev.type()) { + + case os::Event::CloseApp: + case os::Event::CloseWindow: + running = false; + break; + + case os::Event::ResizeWindow: + redraw_window(ev.window().get()); + ev.window()->invalidate(); + break; + + default: + // Do nothing + break; + } + } + + return 0; +} From 149ad4c0dddbce1b5e206e683531a62d10c56d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Thu, 11 Apr 2024 10:49:59 -0300 Subject: [PATCH 04/41] [win] Add drag and drop implementation for Windows along some required Skia layer changes --- os/CMakeLists.txt | 1 + os/none/os.cpp | 3 + os/skia/skia_surface.cpp | 126 ++++++++++++++- os/skia/skia_surface.h | 8 +- os/skia/skia_system.h | 11 +- os/surface_format.h | 10 ++ os/system.h | 3 +- os/win/dnd.cpp | 335 +++++++++++++++++++++++++++++++++++++++ os/win/dnd.h | 65 ++++++++ os/win/system.cpp | 21 +++ os/win/system.h | 3 + os/win/window.cpp | 14 +- os/win/window.h | 3 + 13 files changed, 596 insertions(+), 7 deletions(-) create mode 100644 os/win/dnd.cpp create mode 100644 os/win/dnd.h diff --git a/os/CMakeLists.txt b/os/CMakeLists.txt index 4c7f802a8..163c7a412 100644 --- a/os/CMakeLists.txt +++ b/os/CMakeLists.txt @@ -13,6 +13,7 @@ set(LAF_OS_SOURCES if(WIN32) list(APPEND LAF_OS_SOURCES win/color_space.cpp + win/dnd.cpp win/event_queue.cpp win/keys.cpp win/native_dialogs.cpp diff --git a/os/none/os.cpp b/os/none/os.cpp index 85d0d16f7..bc194bb76 100644 --- a/os/none/os.cpp +++ b/os/none/os.cpp @@ -42,6 +42,9 @@ class NoneSystem : public System { Ref makeWindow(const WindowSpec& spec) override { return nullptr; } Ref makeSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { return nullptr; } + Ref makeSurface(int width, int height, + const os::SurfaceFormatData& sf, + const unsigned char* data = nullptr) override { return nullptr; } Ref makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { return nullptr; } Ref loadSurface(const char* filename) override { return nullptr; } diff --git a/os/skia/skia_surface.cpp b/os/skia/skia_surface.cpp index 8feb004a4..f461bbd7a 100644 --- a/os/skia/skia_surface.cpp +++ b/os/skia/skia_surface.cpp @@ -1,5 +1,5 @@ // LAF OS Library -// Copyright (c) 2018-2022 Igara Studio S.A. +// Copyright (c) 2018-2024 Igara Studio S.A. // Copyright (c) 2016-2018 David Capello // // This file is released under the terms of the MIT license. @@ -14,12 +14,17 @@ #include "base/file_handle.h" #include "gfx/path.h" #include "os/skia/skia_helpers.h" +#include "os/surface_format.h" #include "os/system.h" +#include "include/core/SkAlphaType.h" #include "include/codec/SkCodec.h" #include "include/core/SkCanvas.h" #include "include/core/SkColorFilter.h" +#include "include/core/SkImageInfo.h" #include "include/core/SkPixelRef.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkSize.h" #include "include/core/SkStream.h" #include "include/private/SkColorData.h" @@ -29,6 +34,7 @@ #endif #include +#include namespace os { @@ -76,6 +82,40 @@ void SkiaSurface::create(int width, int height, const os::ColorSpaceRef& cs) swapBitmap(bmp); } +void SkiaSurface::create(int width, int height, + const os::SurfaceFormatData& sf, + const unsigned char* data) +{ + destroy(); + + ASSERT(!m_surface) + ASSERT(width > 0); + ASSERT(height > 0); + + SkColorType ct = SkiaSurface::deductSkColorType(sf); + SkAlphaType at = SkiaSurface::asSkAlphaType(sf.pixelAlpha); + + SkBitmap bmp; + if (!bmp.tryAllocPixels( + SkImageInfo::Make(width, height, ct, at))) + throw base::Exception("Cannot create Skia surface"); + + if (data) { + // Convert 24bpp data to 32bpp data because Skia doesn't work with 24bpp + // images. + if (sf.bitsPerPixel == 24) { + for (int i=0,j=0; j(bmp.getPixels())[i], &data[j], 3); + } + else + bmp.writePixels(SkPixmap(bmp.info(), data, size_t(width) * size_t(sf.bitsPerPixel / 8))); + } + else { + bmp.eraseColor(SK_ColorTRANSPARENT); + } + swapBitmap(bmp); +} + void SkiaSurface::createRgba(int width, int height, const os::ColorSpaceRef& cs) { destroy(); @@ -271,6 +311,7 @@ void SkiaSurface::getFormat(SurfaceFormatData* formatData) const { formatData->format = kRgbaSurfaceFormat; formatData->bitsPerPixel = 8*m_bitmap.bytesPerPixel(); + formatData->pixelAlpha = SkiaSurface::asPixelAlpha(m_bitmap.alphaType()); switch (m_bitmap.colorType()) { case kRGB_565_SkColorType: @@ -326,6 +367,89 @@ void SkiaSurface::getFormat(SurfaceFormatData* formatData) const } } +// static +SkColorType SkiaSurface::deductSkColorType(const os::SurfaceFormatData& sf) +{ + if (sf.redShift == SK_RGBA_R32_SHIFT && + sf.greenShift == SK_RGBA_G32_SHIFT && + sf.blueShift == SK_RGBA_B32_SHIFT && + sf.alphaShift == SK_RGBA_A32_SHIFT && + sf.redMask == (255 << SK_RGBA_R32_SHIFT) && + sf.greenMask == (255 << SK_RGBA_G32_SHIFT) && + sf.blueMask == (255 << SK_RGBA_B32_SHIFT) && + sf.alphaMask == (255 << SK_RGBA_A32_SHIFT)) + return kRGBA_8888_SkColorType; + + if (sf.redShift == SK_RGBA_R32_SHIFT && + sf.greenShift == SK_RGBA_G32_SHIFT && + sf.blueShift == SK_RGBA_B32_SHIFT && + sf.redMask == (255 << SK_RGBA_R32_SHIFT) && + sf.greenMask == (255 << SK_RGBA_G32_SHIFT) && + sf.blueMask == (255 << SK_RGBA_B32_SHIFT) && + sf.alphaMask == 0) + return kRGB_888x_SkColorType; + + if (sf.redShift == SK_BGRA_R32_SHIFT && + sf.greenShift == SK_BGRA_G32_SHIFT && + sf.blueShift == SK_BGRA_B32_SHIFT && + sf.alphaShift == SK_BGRA_A32_SHIFT && + sf.redMask == (255 << SK_BGRA_R32_SHIFT) && + sf.greenMask == (255 << SK_BGRA_G32_SHIFT) && + sf.blueMask == (255 << SK_BGRA_B32_SHIFT) && + sf.alphaMask == (255 << SK_BGRA_A32_SHIFT)) + return kBGRA_8888_SkColorType; + + if (sf.redShift == SK_R4444_SHIFT && + sf.greenShift == SK_G4444_SHIFT && + sf.blueShift == SK_B4444_SHIFT && + sf.alphaShift == SK_A4444_SHIFT && + sf.redMask == (15 << SK_R4444_SHIFT) && + sf.greenMask == (15 << SK_G4444_SHIFT) && + sf.blueMask == (15 << SK_B4444_SHIFT) && + sf.alphaMask == (15 << SK_A4444_SHIFT)) + return kARGB_4444_SkColorType; + + if (sf.redShift == SK_R16_SHIFT && + sf.greenShift == SK_G16_SHIFT && + sf.blueShift == SK_B16_SHIFT && + sf.alphaShift == 0 && + sf.redMask == SK_R16_MASK && + sf.greenMask == SK_G16_MASK && + sf.blueMask == SK_B16_MASK && + sf.alphaMask == 0) + return kRGB_565_SkColorType; + + return kUnknown_SkColorType; +} + +// static +os::PixelAlpha SkiaSurface::asPixelAlpha(SkAlphaType at) +{ + switch(at) { + case SkAlphaType::kOpaque_SkAlphaType: + return os::PixelAlpha::kOpaque; + case SkAlphaType::kPremul_SkAlphaType: + return os::PixelAlpha::kPremultiplied; + case SkAlphaType::kUnpremul_SkAlphaType: + return os::PixelAlpha::kStraight; + default: + throw base::Exception("Unsupported alpha type"); + } +} + +// static +SkAlphaType SkiaSurface::asSkAlphaType(os::PixelAlpha pa) +{ + switch(pa) { + case os::PixelAlpha::kOpaque: + return SkAlphaType::kOpaque_SkAlphaType; + case os::PixelAlpha::kPremultiplied: + return SkAlphaType::kPremul_SkAlphaType; + case os::PixelAlpha::kStraight: + return SkAlphaType::kUnpremul_SkAlphaType; + } +} + gfx::Color SkiaSurface::getPixel(int x, int y) const { // Clip input to avoid crash on SkBitmap::getColor() diff --git a/os/skia/skia_surface.h b/os/skia/skia_surface.h index 738e50bea..2ec52182a 100644 --- a/os/skia/skia_surface.h +++ b/os/skia/skia_surface.h @@ -1,5 +1,5 @@ // LAF OS Library -// Copyright (c) 2018-2022 Igara Studio S.A. +// Copyright (c) 2018-2024 Igara Studio S.A. // Copyright (c) 2012-2018 David Capello // // This file is released under the terms of the MIT license. @@ -15,8 +15,10 @@ #include "os/common/generic_surface.h" #include "os/common/sprite_sheet_font.h" #include "os/skia/skia_color_space.h" +#include "os/surface_format.h" #include "include/core/SkBitmap.h" +#include "include/core/SkColorType.h" #include "include/core/SkPaint.h" #include "include/core/SkSurface.h" @@ -31,6 +33,7 @@ class SkiaSurface final : public Surface { ~SkiaSurface(); void create(int width, int height, const os::ColorSpaceRef& cs); + void create(int width, int height, const SurfaceFormatData& sf, const unsigned char* data); void createRgba(int width, int height, const os::ColorSpaceRef& cs); void destroy(); @@ -115,6 +118,9 @@ class SkiaSurface final : public Surface { static SurfaceRef loadSurface(const char* filename); private: + static SkColorType deductSkColorType(const os::SurfaceFormatData& sf); + static os::PixelAlpha asPixelAlpha(SkAlphaType at); + static SkAlphaType asSkAlphaType(os::PixelAlpha pa); void skDrawSurface( const Surface* src, const gfx::Clip& clip, diff --git a/os/skia/skia_system.h b/os/skia/skia_system.h index fb38fd548..8e85c6ce8 100644 --- a/os/skia/skia_system.h +++ b/os/skia/skia_system.h @@ -1,5 +1,5 @@ // LAF OS Library -// Copyright (C) 2018-2023 Igara Studio S.A. +// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. @@ -16,6 +16,7 @@ #include "os/skia/skia_font_manager.h" #include "os/skia/skia_surface.h" #include "os/skia/skia_window.h" +#include "os/surface_format.h" #include "os/window_spec.h" #if LAF_WINDOWS @@ -103,6 +104,14 @@ class SkiaSystem final : public SkiaSystemBase { return sur; } + SurfaceRef makeSurface(int width, int height, + const os::SurfaceFormatData& sf, + const unsigned char* data) override { + auto sur = make_ref(); + sur->create(width, height, sf, data); + return sur; + } + SurfaceRef makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { auto sur = make_ref(); diff --git a/os/surface_format.h b/os/surface_format.h index 0d9654ad1..3695081e2 100644 --- a/os/surface_format.h +++ b/os/surface_format.h @@ -1,4 +1,5 @@ // LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. // Copyright (C) 2012-2013 David Capello // // This file is released under the terms of the MIT license. @@ -8,12 +9,20 @@ #define OS_SURFACE_FORMAT_H_INCLUDED #pragma once +#include + namespace os { enum SurfaceFormat { kRgbaSurfaceFormat, }; + enum class PixelAlpha { + kOpaque, + kPremultiplied, + kStraight, + }; + struct SurfaceFormatData { SurfaceFormat format; uint32_t bitsPerPixel; @@ -25,6 +34,7 @@ namespace os { uint32_t greenMask; uint32_t blueMask; uint32_t alphaMask; + PixelAlpha pixelAlpha; }; } // namespace os diff --git a/os/system.h b/os/system.h index 1d6f9f74a..3b46ce78d 100644 --- a/os/system.h +++ b/os/system.h @@ -1,5 +1,5 @@ // LAF OS Library -// Copyright (C) 2018-2022 Igara Studio S.A. +// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. @@ -162,6 +162,7 @@ namespace os { } virtual Ref makeSurface(int width, int height, const os::ColorSpaceRef& colorSpace = nullptr) = 0; + virtual Ref makeSurface(int width, int height, const os::SurfaceFormatData& sf, const unsigned char* data = nullptr) = 0; virtual Ref makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace = nullptr) = 0; virtual Ref loadSurface(const char* filename) = 0; virtual Ref loadRgbaSurface(const char* filename) = 0; diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp new file mode 100644 index 000000000..0dc1714f5 --- /dev/null +++ b/os/win/dnd.cpp @@ -0,0 +1,335 @@ +// LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#include "os/window.h" +#include "os/win/dnd.h" +#include "os/system.h" + +#include "clip/clip.h" +#include "clip/clip_win.h" + +#include + +DWORD as_dropeffect(const os::DropOperation op) +{ + DWORD effect = DROPEFFECT_NONE; + if (op & os::DropOperation::Copy) + effect |= DROPEFFECT_COPY; + + if (op & os::DropOperation::Move) + effect |= DROPEFFECT_MOVE; + + if (op & os::DropOperation::Link) + effect |= DROPEFFECT_LINK; + + return effect; +} + +os::DropOperation as_dropoperation(DWORD pdwEffect) +{ + int op = 0; + if (pdwEffect & DROPEFFECT_COPY) + op |= os::DropOperation::Copy; + + if (pdwEffect & DROPEFFECT_MOVE) + op |= os::DropOperation::Move; + + if (pdwEffect & DROPEFFECT_LINK) + op |= os::DropOperation::Link; + + return static_cast(op); +} + + +gfx::Point drag_position(HWND hwnd, POINTL& pt) +{ + ScreenToClient(hwnd, (LPPOINT) &pt); + return gfx::Point(pt.x, pt.y); +} + +namespace os { + +// HGLOBAL Locking/Unlocking wrapper +template +class GLock { +public: + GLock() = delete; + GLock(const GLock&) = delete; + GLock(HGLOBAL hglobal) : m_hmem(hglobal) { + m_data = static_cast(GlobalLock(m_hmem)); + } + + ~GLock() { + GlobalUnlock(m_hmem); + } + + operator HGLOBAL() { + return m_hmem; + } + + operator T () { + return m_data; + } + + SIZE_T size() { + return GlobalSize(m_hmem); + } + +private: + HGLOBAL m_hmem; + T m_data; +}; + +base::paths DragDataProviderWin::getPaths() +{ + base::paths files; + FORMATETC fmt; + fmt.cfFormat = CF_HDROP; + fmt.ptd = nullptr; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED::TYMED_HGLOBAL; + STGMEDIUM medium; + if (m_data->GetData(&fmt, &medium) == S_OK) { + { + GLock hdrop(medium.hGlobal); + if (static_cast(hdrop)) { + int count = DragQueryFile(hdrop, 0xFFFFFFFF, nullptr, 0); + for (int index = 0; index < count; ++index) { + int length = DragQueryFile(hdrop, index, nullptr, 0); + if (length > 0) { + std::vector str(length + 1); + DragQueryFile(hdrop, index, &str[0], str.size()); + files.push_back(base::to_utf8(&str[0])); + } + } + } + } + ReleaseStgMedium(&medium); + } + return files; +} + +SurfaceRef DragDataProviderWin::getImage() +{ + SurfaceRef surface = nullptr; + clip::image_spec spec; + clip::image img; + SurfaceFormatData sfd; + + STGMEDIUM medium; + medium.hGlobal = nullptr; + FORMATETC fmt; + fmt.ptd = nullptr; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_HGLOBAL; + + UINT png_format = RegisterClipboardFormatA("PNG"); + if (png_format) { + fmt.cfFormat = png_format; + if (m_data->GetData(&fmt, &medium) == S_OK) { + GLock png_handle(medium.hGlobal); + if (clip::win::read_png(png_handle, png_handle.size(), &img, nullptr)) { + spec = img.spec(); + goto makeSurface; + } + } + } + + fmt.cfFormat = CF_DIBV5; + if (m_data->GetData(&fmt, &medium) == S_OK) { + GLock b5(medium.hGlobal); + clip::win::BitmapInfo bi(b5); + if (bi.to_image(img)) { + spec = img.spec(); + goto makeSurface; + } + } + + fmt.cfFormat = CF_DIB; + if (m_data->GetData(&fmt, &medium) == S_OK) { + GLock hbi(medium.hGlobal); + clip::win::BitmapInfo bi(hbi); + if (bi.to_image(img)) { + spec = img.spec(); + goto makeSurface; + } + } + + // No suitable image format found, release medium and return. + goto releaseMedium; + +makeSurface: + sfd.bitsPerPixel = spec.bits_per_pixel; + sfd.redMask = spec.red_mask; + sfd.greenMask = spec.green_mask; + sfd.blueMask = spec.blue_mask; + sfd.alphaMask = spec.alpha_mask; + sfd.redShift = spec.red_shift; + sfd.greenShift = spec.green_shift; + sfd.blueShift = spec.blue_shift; + sfd.alphaShift = spec.alpha_shift; + sfd.pixelAlpha = PixelAlpha::kStraight; + surface = os::instance()->makeSurface(spec.width, spec.height, sfd, (unsigned char*)img.data()); + +releaseMedium: + ReleaseStgMedium(&medium); + return surface; +} + +bool DragDataProviderWin::contains(DragDataItemType type) +{ + base::ComPtr formats; + if (m_data->EnumFormatEtc(DATADIR::DATADIR_GET, &formats) != S_OK) + return false; + + char name[101]; + FORMATETC fmt; + while (formats->Next(1, &fmt, nullptr) == S_OK) { + switch (fmt.cfFormat) { + case CF_HDROP: + if (type == DragDataItemType::Paths) + return true; + break; + case CF_DIBV5: + if (type == DragDataItemType::Image) + return true; + break; + case CF_DIB: + if (type == DragDataItemType::Image) + return true; + break; + default: + int namelen = GetClipboardFormatNameA(fmt.cfFormat, name, 100); + name[namelen] = '\0'; + if (std::strcmp(name, "PNG") == 0 && type == DragDataItemType::Image) + return true; + } + } + return false; +} + +STDMETHODIMP DragTargetAdapter::QueryInterface(REFIID riid, LPVOID* ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + if (riid != IID_IDropTarget && riid != IID_IUnknown) + return E_NOINTERFACE; + + *ppv = static_cast(this); + AddRef(); + return NOERROR; +} + +ULONG DragTargetAdapter::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DragTargetAdapter::Release() +{ + // Decrement the object's internal counter. + ULONG ref = InterlockedDecrement(&m_ref); + if (0 == ref) + delete this; + + return ref; +} + +STDMETHODIMP DragTargetAdapter::DragEnter(IDataObject* pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) +{ + if (!m_window->hasDragTarget()) + return E_NOTIMPL; + + m_data = base::ComPtr(pDataObj); + if (!m_data) + return E_UNEXPECTED; + + m_position = drag_position((HWND) m_window->nativeHandle(), pt); + auto ddProvider = std::make_unique(m_data.get()); + DragEvent ev(m_window, + as_dropoperation(*pdwEffect), + m_position, + ddProvider.get()); + + m_window->notifyDragEnter(ev); + + *pdwEffect = as_dropeffect(ev.dropResult()); + + return S_OK; +} + +STDMETHODIMP DragTargetAdapter::DragOver(DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) +{ + if (!m_window->hasDragTarget()) + return E_NOTIMPL; + + m_position = drag_position((HWND)m_window->nativeHandle(), pt); + auto ddProvider = std::make_unique(m_data.get()); + DragEvent ev(m_window, + as_dropoperation(*pdwEffect), + m_position, + ddProvider.get()); + + m_window->notifyDrag(ev); + + *pdwEffect = as_dropeffect(ev.dropResult()); + + return S_OK; +} + +STDMETHODIMP DragTargetAdapter::DragLeave(void) +{ + if (!m_window->hasDragTarget()) + return E_NOTIMPL; + + auto ddProvider = std::make_unique(m_data.get()); + os::DragEvent ev(m_window, + DropOperation::None, + m_position, + ddProvider.get()); + m_window->notifyDragLeave(ev); + + m_data.reset(); + return S_OK; +} + +STDMETHODIMP DragTargetAdapter::Drop(IDataObject* pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) +{ + if (!m_window->hasDragTarget()) + return E_NOTIMPL; + + m_data = base::ComPtr(pDataObj); + if (!m_data) + return E_UNEXPECTED; + + m_position = drag_position((HWND)m_window->nativeHandle(), pt); + auto ddProvider = std::make_unique(m_data.get()); + DragEvent ev(m_window, + as_dropoperation(*pdwEffect), + m_position, + ddProvider.get()); + + m_window->notifyDrop(ev); + + m_data = nullptr; + *pdwEffect = as_dropeffect(ev.dropResult()); + return S_OK; +} + + +} // namespase os diff --git a/os/win/dnd.h b/os/win/dnd.h new file mode 100644 index 000000000..b915ba33b --- /dev/null +++ b/os/win/dnd.h @@ -0,0 +1,65 @@ +// LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef OS_WIN_DND_H_INCLUDED +#define OS_WIN_DND_H_INCLUDED +#pragma once + +#include "base/win/comptr.h" +#include "os/dnd.h" + +#include + +namespace os { + + class DragDataProviderWin : public DragDataProvider { + public: + DragDataProviderWin(IDataObject* pDataObj) : m_data(pDataObj) { } + + private: + IDataObject* m_data; + + base::paths getPaths() override; + SurfaceRef getImage() override; + bool contains(DragDataItemType type) override; + }; + + // IDropTarget implementation used to adapt the OLE's Drag & Drop interface to + // Laf's DragTarget interface. + class DragTargetAdapter : public IDropTarget { + public: + DragTargetAdapter(Window* window) : m_window(window) { } + + // IUnknown methods + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppv) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + // IDropTarget methods + HRESULT STDMETHODCALLTYPE DragEnter(IDataObject* pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) override; + HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) override; + HRESULT STDMETHODCALLTYPE DragLeave(void) override; + HRESULT STDMETHODCALLTYPE Drop(IDataObject* pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) override; + + private: + ULONG m_ref = 0; + Window* m_window = nullptr; + // Pointer to data being dragged + base::ComPtr m_data; + // Last mouse position when dragging data over the target. + gfx::Point m_position = gfx::Point(0, 0); + }; +} // namespace os + +#endif diff --git a/os/win/system.cpp b/os/win/system.cpp index 698128c7b..d07f071e5 100644 --- a/os/win/system.cpp +++ b/os/win/system.cpp @@ -8,6 +8,7 @@ #include "config.h" #endif +#include "base/log.h" #include "os/win/system.h" #include "gfx/color.h" @@ -15,6 +16,9 @@ #include +#pragma push_macro("ERROR") +#undef ERROR + namespace os { bool win_is_key_pressed(KeyScancode scancode); @@ -155,9 +159,24 @@ SystemWin::SystemWin() SystemWin::~SystemWin() { + if (m_appMode == AppMode::GUI) { + OleUninitialize(); + } destroyInstance(); } +void SystemWin::setAppMode(AppMode appMode) +{ + m_appMode = appMode; + if (m_appMode == AppMode::GUI) { + auto result = OleInitialize(nullptr); + if (result != S_OK && result != S_FALSE) { + LOG(LogLevel::ERROR, "WIN: Could not initialize OLE (%d)", result); + return; + } + } +} + void SystemWin::setAppName(const std::string& appName) { m_appName = appName; @@ -316,3 +335,5 @@ void SystemWin::_setInternalMousePosition(const Event& ev) } } // namespace os + +#pragma pop_macro("ERROR") diff --git a/os/win/system.h b/os/win/system.h index 103663f71..db363c904 100644 --- a/os/win/system.h +++ b/os/win/system.h @@ -20,6 +20,8 @@ class SystemWin : public CommonSystem { SystemWin(); ~SystemWin(); + void setAppMode(AppMode appMode) override; + WinAPI& winApi() { return m_winApi; } WintabAPI& wintabApi() { return m_wintabApi; } @@ -59,6 +61,7 @@ class SystemWin : public CommonSystem { WinAPI m_winApi; WintabAPI m_wintabApi; gfx::Point m_screenMousePos; + AppMode m_appMode; }; } // namespace os diff --git a/os/win/window.cpp b/os/win/window.cpp index 09344eb6a..1ff0a56fc 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -277,6 +277,11 @@ WindowWin::WindowWin(const WindowSpec& spec) // WindowSpec::activate() flag for this) m_activate = (spec.parent() == nullptr); + auto result = RegisterDragDrop(m_hwnd, reinterpret_cast(&m_dragTargetAdapter)); + if (result != S_OK) { + LOG(LogLevel::ERROR, "Could not register window for Drag & Drop: %d\n", result); + } + // Log information about the system (for debugging/user support // purposes in case the window doesn't display anything) if (base::get_log_level() >= INFO) { @@ -318,7 +323,9 @@ WindowWin::~WindowWin() // If this assert fails it's highly probable that an os::WindowRef // was kept alive in some kind of memory leak (or just inside an - // os::Event in the os::EventQueue). + // os::Event in the os::EventQueue). Also this can happen when + // declaring a os::WindowRef before calling os::make_system(), + // because of deletion order when destructors got called. ASSERT(sys); if (sys) { @@ -343,8 +350,9 @@ os::ScreenRef WindowWin::screen() const os::ColorSpaceRef WindowWin::colorSpace() const { - if (auto defaultCS = os::instance()->windowsColorSpace()) - return defaultCS; + if (os::instance()) + if (auto defaultCS = os::instance()->windowsColorSpace()) + return defaultCS; if (m_hwnd) { HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST); diff --git a/os/win/window.h b/os/win/window.h index 69d4eb0ee..a5f0f42cd 100644 --- a/os/win/window.h +++ b/os/win/window.h @@ -16,6 +16,7 @@ #include "os/native_cursor.h" #include "os/pointer_type.h" #include "os/screen.h" +#include "os/win/dnd.h" #include "os/win/wintab.h" #include @@ -138,6 +139,8 @@ namespace os { gfx::Rect m_restoredFrame; #endif + DragTargetAdapter m_dragTargetAdapter = DragTargetAdapter(this); + int m_scale; bool m_isCreated; // Since Windows Vista, it looks like Microsoft decided to change From f46a9d999275dbed105c34285951320f9b9b7b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 12 Apr 2024 15:42:57 -0300 Subject: [PATCH 05/41] [osx] Add drag and drop implementation for osx --- os/osx/dnd.h | 129 +++++++++++++++++++++++++++++++++++++++++++++++++ os/osx/view.h | 2 + os/osx/view.mm | 84 ++++++++++++++++++++++++++++---- 3 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 os/osx/dnd.h diff --git a/os/osx/dnd.h b/os/osx/dnd.h new file mode 100644 index 000000000..54eddc306 --- /dev/null +++ b/os/osx/dnd.h @@ -0,0 +1,129 @@ +// LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef OS_OSX_DND_H_INCLUDED +#define OS_OSX_DND_H_INCLUDED +#pragma once + +#ifdef __OBJC__ + +#include "base/exception.h" +#include "base/fs.h" +#include "base/paths.h" +#include "clip/clip.h" +#include "clip/clip_osx.h" +#include "gfx/point.h" +#include "os/dnd.h" +#include "os/surface.h" +#include "os/surface_format.h" +#include "os/system.h" + +#include +#include + +namespace os { + class DragDataProviderOSX : public DragDataProvider { + public: + DragDataProviderOSX(NSPasteboard* pasteboard) : m_pasteboard(pasteboard) {} + + private: + NSPasteboard* m_pasteboard; + + base::paths getPaths() override { + base::paths files; + + if ([m_pasteboard.types containsObject:NSFilenamesPboardType]) { + NSArray* filenames = [m_pasteboard propertyListForType:NSFilenamesPboardType]; + for (int i=0; i<[filenames count]; ++i) { + NSString* fn = [filenames objectAtIndex: i]; + + files.push_back(base::normalize_path([fn UTF8String])); + } + } + return files; + } + + SurfaceRef getImage() override { + clip::image img; + clip::image_spec spec; + if (!clip::osx::get_image_from_clipboard(m_pasteboard, &img, &spec)) + return nullptr; + + os::SurfaceFormatData sfd; + sfd.bitsPerPixel = spec.bits_per_pixel; + sfd.redMask = spec.red_mask; + sfd.greenMask = spec.green_mask; + sfd.blueMask = spec.blue_mask; + sfd.alphaMask = spec.alpha_mask; + sfd.redShift = spec.red_shift; + sfd.greenShift = spec.green_shift; + sfd.blueShift = spec.blue_shift; + sfd.alphaShift = spec.alpha_shift; + sfd.pixelAlpha = PixelAlpha::kStraight; + + SurfaceRef surface = os::instance()->makeSurface( + spec.width, spec.height, + sfd, (unsigned char*) img.data()); + return surface; + } + + bool contains(DragDataItemType type) override { + for (NSPasteboardType t in m_pasteboard.types) { + if (type == DragDataItemType::Paths && + [t isEqual: NSFilenamesPboardType]) + return true; + + if (type == DragDataItemType::Image && + ([t isEqual: NSPasteboardTypeTIFF] || + [t isEqual: NSPasteboardTypePNG])) + return true; + } + return false; + } + }; +} // namespace os + +NSDragOperation as_nsdragoperation(const os::DropOperation op) +{ + NSDragOperation nsdop; + if (op & os::DropOperation::Copy) + nsdop |= NSDragOperationCopy; + + if (op & os::DropOperation::Move) + nsdop |= NSDragOperationMove; + + if (op & os::DropOperation::Link) + nsdop |= NSDragOperationLink; + + return nsdop; +} + +os::DropOperation as_dropoperation(const NSDragOperation nsdop) +{ + int op = 0; + if (nsdop & NSDragOperationCopy) + op |= os::DropOperation::Copy; + + if (nsdop & NSDragOperationMove) + op |= os::DropOperation::Move; + + if (nsdop & NSDragOperationLink) + op |= os::DropOperation::Link; + + return static_cast(op); +} + +gfx::Point drag_position(id sender) +{ + NSRect contentRect = [sender.draggingDestinationWindow contentRectForFrameRect:sender.draggingDestinationWindow.frame]; + return gfx::Point( + sender.draggingLocation.x, + contentRect.size.height - sender.draggingLocation.y); +} + +#endif + +#endif diff --git a/os/osx/view.h b/os/osx/view.h index 846039606..1ba0cba09 100644 --- a/os/osx/view.h +++ b/os/osx/view.h @@ -66,6 +66,8 @@ namespace os { - (void)destroyMouseTrackingArea; - (void)updateCurrentCursor; - (NSDragOperation)draggingEntered:(id)sender; +- (NSDragOperation)draggingUpdated:(id)sender; +- (void)draggingExited:(id)sender; - (BOOL)performDragOperation:(id)sender; - (void)doCommandBySelector:(SEL)selector; - (void)setTranslateDeadKeys:(BOOL)state; diff --git a/os/osx/view.mm b/os/osx/view.mm index 8856112f8..0e939b384 100644 --- a/os/osx/view.mm +++ b/os/osx/view.mm @@ -1,5 +1,5 @@ // LAF OS Library -// Copyright (C) 2018-2023 Igara Studio S.A. +// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2015-2018 David Capello // // This file is released under the terms of the MIT license. @@ -15,12 +15,16 @@ #include "base/debug.h" #include "gfx/point.h" +#include "os/osx/dnd.h" #include "os/event.h" #include "os/event_queue.h" #include "os/osx/generate_drop_files.h" #include "os/osx/keys.h" #include "os/osx/window.h" #include "os/system.h" +#include "os/window.h" + +#include namespace os { @@ -144,6 +148,8 @@ - (id)initWithFrame:(NSRect)frameRect [self registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, + NSPasteboardTypePNG, + NSPasteboardTypeTIFF, nil]]; // Create a CALayer for backing content with async drawing. This @@ -652,26 +658,84 @@ - (void)updateCurrentCursor - (NSDragOperation)draggingEntered:(id)sender { - return NSDragOperationCopy; + WindowOSXObjc* target = (WindowOSXObjc*)sender.draggingDestinationWindow; + + if (!target || ![target impl]->hasDragTarget()) + return NSDragOperationCopy; + + NSPasteboard* pasteboard = [sender draggingPasteboard]; + os::Window* window = [target impl]; + auto ddProvider = std::make_unique(pasteboard); + os::DragEvent ev(window, + as_dropoperation([sender draggingSourceOperationMask]), + drag_position(sender), + ddProvider.get()); + window->notifyDragEnter(ev); + return as_nsdragoperation(ev.dropResult()); +} + +- (NSDragOperation)draggingUpdated:(id)sender +{ + NSDragOperation value = [super draggingUpdated:sender]; + WindowOSXObjc* target = (WindowOSXObjc*)sender.draggingDestinationWindow; + + if (!target || ![target impl]->hasDragTarget()) + return value; + + NSPasteboard* pasteboard = [sender draggingPasteboard]; + os::Window* window = [target impl]; + auto ddProvider = std::make_unique(pasteboard); + os::DragEvent ev(window, + as_dropoperation([sender draggingSourceOperationMask]), + drag_position(sender), + ddProvider.get()); + ev.dropResult(as_dropoperation(value)); + window->notifyDrag(ev); + return as_nsdragoperation(ev.dropResult()); +} + +- (void)draggingExited:(id)sender +{ + WindowOSXObjc* target = (WindowOSXObjc*)sender.draggingDestinationWindow; + + if (!target || ![target impl]->hasDragTarget()) + return; + + NSPasteboard* pasteboard = [sender draggingPasteboard]; + os::Window* window = [target impl]; + auto ddProvider = std::make_unique(pasteboard); + os::DragEvent ev(window, + as_dropoperation([sender draggingSourceOperationMask]), + drag_position(sender), + ddProvider.get()); + window->notifyDragLeave(ev); } - (BOOL)performDragOperation:(id )sender { NSPasteboard* pasteboard = [sender draggingPasteboard]; + WindowOSXObjc* target = (WindowOSXObjc*)sender.draggingDestinationWindow; - if ([pasteboard.types containsObject:NSFilenamesPboardType]) { - NSArray* filenames = [pasteboard propertyListForType:NSFilenamesPboardType]; + // Try to generate a DropFiles event if no DragTarget is defined. + if (!target || ![target impl]->hasDragTarget()) { + if (![pasteboard.types containsObject:NSFilenamesPboardType]) + return NO; + NSArray* filenames = [pasteboard propertyListForType:NSFilenamesPboardType]; os::Event ev = generate_drop_files_from_nsarray(filenames); - NSRect contentRect = [sender.draggingDestinationWindow contentRectForFrameRect: sender.draggingDestinationWindow.frame]; - ev.setPosition(gfx::Point( - sender.draggingLocation.x, - contentRect.size.height - sender.draggingLocation.y)); + ev.setPosition(drag_position(sender)); [self queueEvent:ev]; return YES; } - else - return NO; + + os::Window* window = [target impl]; + auto ddProvider = std::make_unique(pasteboard); + os::DragEvent ev(window, + as_dropoperation([sender draggingSourceOperationMask]), + drag_position(sender), + ddProvider.get()); + window->notifyDrop(ev); + return ev.acceptDrop(); } - (void)doCommandBySelector:(SEL)selector From e0dfd8cb50689a469e092939ffaa3acedaea928a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 15:40:23 -0300 Subject: [PATCH 06/41] Anchor the "drop here!" box to the top-right corner of the window --- examples/drag_and_drop.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index 0e140e7c2..95b9420dd 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -130,6 +130,7 @@ static void redraw_window(os::Window* window) auto zoneColor = gfx::rgba(100, 255, 100); auto textColor = zoneColor; + windowData.dropZone.x = window->width() - windowData.dropZone.w - 12; if (windowData.dropZone.contains(windowData.dragPosition)){ paint.style(os::Paint::Style::Fill); paint.color(zoneColor); @@ -155,7 +156,7 @@ static os::WindowRef create_window(const std::string& title, newWindow->setTitle(title); newWindow->setVisible(true); newWindow->setDragTarget(&dragTarget); - windowData.dropZone = {32, spec.frame().h - 64 - 40, 64, 64}; + windowData.dropZone = {spec.frame().w - 64 - 12, 12, 64, 64}; redraw_window(newWindow.get()); return newWindow; } From cbb79e8721934ab27168403328696c462ffdfd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 15:56:27 -0300 Subject: [PATCH 07/41] [osx] Separate DragDataProviderOSX implementation from its header file --- os/CMakeLists.txt | 1 + os/osx/dnd.h | 99 +++------------------------------------ os/osx/dnd.mm | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 os/osx/dnd.mm diff --git a/os/CMakeLists.txt b/os/CMakeLists.txt index 163c7a412..e36f91eaa 100644 --- a/os/CMakeLists.txt +++ b/os/CMakeLists.txt @@ -29,6 +29,7 @@ elseif(APPLE) list(APPEND LAF_OS_SOURCES osx/app.mm osx/app_delegate.mm + osx/dnd.mm osx/color_space.mm osx/event_queue.mm osx/keys.mm diff --git a/os/osx/dnd.h b/os/osx/dnd.h index 54eddc306..d284ba9ed 100644 --- a/os/osx/dnd.h +++ b/os/osx/dnd.h @@ -10,19 +10,12 @@ #ifdef __OBJC__ -#include "base/exception.h" -#include "base/fs.h" #include "base/paths.h" -#include "clip/clip.h" -#include "clip/clip_osx.h" #include "gfx/point.h" #include "os/dnd.h" #include "os/surface.h" -#include "os/surface_format.h" -#include "os/system.h" #include -#include namespace os { class DragDataProviderOSX : public DragDataProvider { @@ -32,97 +25,17 @@ namespace os { private: NSPasteboard* m_pasteboard; - base::paths getPaths() override { - base::paths files; + base::paths getPaths() override; - if ([m_pasteboard.types containsObject:NSFilenamesPboardType]) { - NSArray* filenames = [m_pasteboard propertyListForType:NSFilenamesPboardType]; - for (int i=0; i<[filenames count]; ++i) { - NSString* fn = [filenames objectAtIndex: i]; + SurfaceRef getImage() override; - files.push_back(base::normalize_path([fn UTF8String])); - } - } - return files; - } - - SurfaceRef getImage() override { - clip::image img; - clip::image_spec spec; - if (!clip::osx::get_image_from_clipboard(m_pasteboard, &img, &spec)) - return nullptr; - - os::SurfaceFormatData sfd; - sfd.bitsPerPixel = spec.bits_per_pixel; - sfd.redMask = spec.red_mask; - sfd.greenMask = spec.green_mask; - sfd.blueMask = spec.blue_mask; - sfd.alphaMask = spec.alpha_mask; - sfd.redShift = spec.red_shift; - sfd.greenShift = spec.green_shift; - sfd.blueShift = spec.blue_shift; - sfd.alphaShift = spec.alpha_shift; - sfd.pixelAlpha = PixelAlpha::kStraight; - - SurfaceRef surface = os::instance()->makeSurface( - spec.width, spec.height, - sfd, (unsigned char*) img.data()); - return surface; - } - - bool contains(DragDataItemType type) override { - for (NSPasteboardType t in m_pasteboard.types) { - if (type == DragDataItemType::Paths && - [t isEqual: NSFilenamesPboardType]) - return true; - - if (type == DragDataItemType::Image && - ([t isEqual: NSPasteboardTypeTIFF] || - [t isEqual: NSPasteboardTypePNG])) - return true; - } - return false; - } + bool contains(DragDataItemType type) override; }; } // namespace os -NSDragOperation as_nsdragoperation(const os::DropOperation op) -{ - NSDragOperation nsdop; - if (op & os::DropOperation::Copy) - nsdop |= NSDragOperationCopy; - - if (op & os::DropOperation::Move) - nsdop |= NSDragOperationMove; - - if (op & os::DropOperation::Link) - nsdop |= NSDragOperationLink; - - return nsdop; -} - -os::DropOperation as_dropoperation(const NSDragOperation nsdop) -{ - int op = 0; - if (nsdop & NSDragOperationCopy) - op |= os::DropOperation::Copy; - - if (nsdop & NSDragOperationMove) - op |= os::DropOperation::Move; - - if (nsdop & NSDragOperationLink) - op |= os::DropOperation::Link; - - return static_cast(op); -} - -gfx::Point drag_position(id sender) -{ - NSRect contentRect = [sender.draggingDestinationWindow contentRectForFrameRect:sender.draggingDestinationWindow.frame]; - return gfx::Point( - sender.draggingLocation.x, - contentRect.size.height - sender.draggingLocation.y); -} +NSDragOperation as_nsdragoperation(const os::DropOperation op); +os::DropOperation as_dropoperation(const NSDragOperation nsdop); +gfx::Point drag_position(id sender); #endif diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm new file mode 100644 index 000000000..864aef2c9 --- /dev/null +++ b/os/osx/dnd.mm @@ -0,0 +1,116 @@ +// LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#include "base/exception.h" +#include "base/fs.h" +#include "clip/clip.h" +#include "clip/clip_osx.h" +#include "os/osx/dnd.h" +#include "os/surface_format.h" +#include "os/system.h" + +#include + +#ifdef __OBJC__ + +namespace os { + +base::paths DragDataProviderOSX::getPaths() +{ + base::paths files; + + if ([m_pasteboard.types containsObject:NSFilenamesPboardType]) { + NSArray* filenames = [m_pasteboard propertyListForType:NSFilenamesPboardType]; + for (int i=0; i<[filenames count]; ++i) { + NSString* fn = [filenames objectAtIndex: i]; + + files.push_back(base::normalize_path([fn UTF8String])); + } + } + return files; +} + +SurfaceRef DragDataProviderOSX::getImage() +{ + clip::image img; + clip::image_spec spec; + if (!clip::osx::get_image_from_clipboard(m_pasteboard, &img, &spec)) + return nullptr; + + os::SurfaceFormatData sfd; + sfd.bitsPerPixel = spec.bits_per_pixel; + sfd.redMask = spec.red_mask; + sfd.greenMask = spec.green_mask; + sfd.blueMask = spec.blue_mask; + sfd.alphaMask = spec.alpha_mask; + sfd.redShift = spec.red_shift; + sfd.greenShift = spec.green_shift; + sfd.blueShift = spec.blue_shift; + sfd.alphaShift = spec.alpha_shift; + sfd.pixelAlpha = PixelAlpha::kStraight; + + SurfaceRef surface = os::instance()->makeSurface( + spec.width, spec.height, + sfd, (unsigned char*) img.data()); + return surface; +} + +bool DragDataProviderOSX::contains(DragDataItemType type) +{ + for (NSPasteboardType t in m_pasteboard.types) { + if (type == DragDataItemType::Paths && + [t isEqual: NSFilenamesPboardType]) + return true; + + if (type == DragDataItemType::Image && + ([t isEqual: NSPasteboardTypeTIFF] || + [t isEqual: NSPasteboardTypePNG])) + return true; + } + return false; +} + +} // namespace os + +NSDragOperation as_nsdragoperation(const os::DropOperation op) +{ + NSDragOperation nsdop; + if (op & os::DropOperation::Copy) + nsdop |= NSDragOperationCopy; + + if (op & os::DropOperation::Move) + nsdop |= NSDragOperationMove; + + if (op & os::DropOperation::Link) + nsdop |= NSDragOperationLink; + + return nsdop; +} + +os::DropOperation as_dropoperation(const NSDragOperation nsdop) +{ + int op = 0; + if (nsdop & NSDragOperationCopy) + op |= os::DropOperation::Copy; + + if (nsdop & NSDragOperationMove) + op |= os::DropOperation::Move; + + if (nsdop & NSDragOperationLink) + op |= os::DropOperation::Link; + + return static_cast(op); +} + +gfx::Point drag_position(id sender) +{ + NSRect contentRect = [sender.draggingDestinationWindow contentRectForFrameRect:sender.draggingDestinationWindow.frame]; + return gfx::Point( + sender.draggingLocation.x, + contentRect.size.height - sender.draggingLocation.y); +} + +#endif From 115be9d2978ae6148be993abe99d755d45ab0378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 16:37:24 -0300 Subject: [PATCH 08/41] Make DropOperation an enum class --- os/dnd.h | 8 +++++--- os/osx/dnd.mm | 12 ++++++------ os/win/dnd.cpp | 12 ++++++------ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/os/dnd.h b/os/dnd.h index d43fee950..e1eb8efdd 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -24,7 +24,7 @@ namespace os { // Operations that can be supported by source and target windows in a drag // and drop operation. - enum DropOperation { + enum class DropOperation { None = 0, Copy = 1, Move = 2, @@ -74,10 +74,12 @@ namespace os { // target window, or set to false otherwise. void acceptDrop(bool value) { m_acceptDrop = value; } - bool sourceSupports(DropOperation op) { return (m_supportedOperations & op) == op; } + bool sourceSupports(DropOperation op) { + return static_cast(m_supportedOperations) & static_cast(op); + } private: - os::Window* m_target; + os::Window* m_target = nullptr; DropOperation m_dropResult = DropOperation::Copy; // Bitwise OR of the operations supported by the drag and drop source. DropOperation m_supportedOperations; diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm index 864aef2c9..37def047c 100644 --- a/os/osx/dnd.mm +++ b/os/osx/dnd.mm @@ -78,13 +78,13 @@ NSDragOperation as_nsdragoperation(const os::DropOperation op) { NSDragOperation nsdop; - if (op & os::DropOperation::Copy) + if (static_cast(op) & static_cast(os::DropOperation::Copy)) nsdop |= NSDragOperationCopy; - if (op & os::DropOperation::Move) + if (static_cast(op) & static_cast(os::DropOperation::Move)) nsdop |= NSDragOperationMove; - if (op & os::DropOperation::Link) + if (static_cast(op) & static_cast(os::DropOperation::Link)) nsdop |= NSDragOperationLink; return nsdop; @@ -94,13 +94,13 @@ NSDragOperation as_nsdragoperation(const os::DropOperation op) { int op = 0; if (nsdop & NSDragOperationCopy) - op |= os::DropOperation::Copy; + op |= static_cast(os::DropOperation::Copy); if (nsdop & NSDragOperationMove) - op |= os::DropOperation::Move; + op |= static_cast(os::DropOperation::Move); if (nsdop & NSDragOperationLink) - op |= os::DropOperation::Link; + op |= static_cast(os::DropOperation::Link); return static_cast(op); } diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 0dc1714f5..4406137d4 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -16,13 +16,13 @@ DWORD as_dropeffect(const os::DropOperation op) { DWORD effect = DROPEFFECT_NONE; - if (op & os::DropOperation::Copy) + if (static_cast(op) & static_cast(os::DropOperation::Copy)) effect |= DROPEFFECT_COPY; - if (op & os::DropOperation::Move) + if (static_cast(op) & static_cast(os::DropOperation::Move)) effect |= DROPEFFECT_MOVE; - if (op & os::DropOperation::Link) + if (static_cast(op) & static_cast(os::DropOperation::Link)) effect |= DROPEFFECT_LINK; return effect; @@ -32,13 +32,13 @@ os::DropOperation as_dropoperation(DWORD pdwEffect) { int op = 0; if (pdwEffect & DROPEFFECT_COPY) - op |= os::DropOperation::Copy; + op |= static_cast(os::DropOperation::Copy); if (pdwEffect & DROPEFFECT_MOVE) - op |= os::DropOperation::Move; + op |= static_cast(os::DropOperation::Move); if (pdwEffect & DROPEFFECT_LINK) - op |= os::DropOperation::Link; + op |= static_cast(os::DropOperation::Link); return static_cast(op); } From ba36dcbc56a05399fdc956f5fb0e92f094a37ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 17:19:18 -0300 Subject: [PATCH 09/41] [win] Enclose internal functions in anonymous namespace --- os/win/dnd.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 4406137d4..6ea0480e5 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -13,6 +13,8 @@ #include +namespace { + DWORD as_dropeffect(const os::DropOperation op) { DWORD effect = DROPEFFECT_NONE; @@ -50,6 +52,8 @@ gfx::Point drag_position(HWND hwnd, POINTL& pt) return gfx::Point(pt.x, pt.y); } +} // anonymous namespace + namespace os { // HGLOBAL Locking/Unlocking wrapper From a1ecee2048ead19045c037022aa930c995fa988a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 18:09:47 -0300 Subject: [PATCH 10/41] Add comment referring Win32 docs about why to add 1 char to the buffer passed to DragQueryFile --- os/win/dnd.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 6ea0480e5..6401c9ea4 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -105,6 +105,8 @@ base::paths DragDataProviderWin::getPaths() for (int index = 0; index < count; ++index) { int length = DragQueryFile(hdrop, index, nullptr, 0); if (length > 0) { + // From Win32 docs: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew + // the DragQueryFile() does't include the null character in its return value. std::vector str(length + 1); DragQueryFile(hdrop, index, &str[0], str.size()); files.push_back(base::to_utf8(&str[0])); From 531b681e09700d031fa9755ea8c77f530688513c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 18:13:23 -0300 Subject: [PATCH 11/41] Use vector's data() method instead of the address of the element on index 0 --- os/win/dnd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 6401c9ea4..d8547b168 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -108,8 +108,8 @@ base::paths DragDataProviderWin::getPaths() // From Win32 docs: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew // the DragQueryFile() does't include the null character in its return value. std::vector str(length + 1); - DragQueryFile(hdrop, index, &str[0], str.size()); - files.push_back(base::to_utf8(&str[0])); + DragQueryFile(hdrop, index, str.data(), str.size()); + files.push_back(base::to_utf8(str.data())); } } } From 2073fd4b48ac42a5683a22bcd80cd4ecef484903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 15 Apr 2024 18:22:37 -0300 Subject: [PATCH 12/41] Several minor changes --- os/win/dnd.cpp | 6 ++++-- os/win/system.cpp | 6 ++---- os/win/window.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index d8547b168..ff1d2aadd 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -209,11 +209,13 @@ bool DragDataProviderWin::contains(DragDataItemType type) if (type == DragDataItemType::Image) return true; break; - default: + default: { int namelen = GetClipboardFormatNameA(fmt.cfFormat, name, 100); name[namelen] = '\0'; if (std::strcmp(name, "PNG") == 0 && type == DragDataItemType::Image) return true; + break; + } } } return false; @@ -260,7 +262,7 @@ STDMETHODIMP DragTargetAdapter::DragEnter(IDataObject* pDataObj, if (!m_data) return E_UNEXPECTED; - m_position = drag_position((HWND) m_window->nativeHandle(), pt); + m_position = drag_position((HWND)m_window->nativeHandle(), pt); auto ddProvider = std::make_unique(m_data.get()); DragEvent ev(m_window, as_dropoperation(*pdwEffect), diff --git a/os/win/system.cpp b/os/win/system.cpp index d07f071e5..be830e436 100644 --- a/os/win/system.cpp +++ b/os/win/system.cpp @@ -169,11 +169,9 @@ void SystemWin::setAppMode(AppMode appMode) { m_appMode = appMode; if (m_appMode == AppMode::GUI) { - auto result = OleInitialize(nullptr); - if (result != S_OK && result != S_FALSE) { + HRESULT result = OleInitialize(nullptr); + if (result != S_OK && result != S_FALSE) LOG(LogLevel::ERROR, "WIN: Could not initialize OLE (%d)", result); - return; - } } } diff --git a/os/win/window.cpp b/os/win/window.cpp index 1ff0a56fc..67d8de816 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -277,7 +277,7 @@ WindowWin::WindowWin(const WindowSpec& spec) // WindowSpec::activate() flag for this) m_activate = (spec.parent() == nullptr); - auto result = RegisterDragDrop(m_hwnd, reinterpret_cast(&m_dragTargetAdapter)); + HRESULT result = RegisterDragDrop(m_hwnd, reinterpret_cast(&m_dragTargetAdapter)); if (result != S_OK) { LOG(LogLevel::ERROR, "Could not register window for Drag & Drop: %d\n", result); } From c1252fd2cf3246df3996dc754305f93142884ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 16 Apr 2024 08:19:33 -0300 Subject: [PATCH 13/41] [win] Move GLock class to anonymous namespace --- os/win/dnd.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index ff1d2aadd..4f7147279 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -52,12 +52,8 @@ gfx::Point drag_position(HWND hwnd, POINTL& pt) return gfx::Point(pt.x, pt.y); } -} // anonymous namespace - -namespace os { - // HGLOBAL Locking/Unlocking wrapper -template +template class GLock { public: GLock() = delete; @@ -66,27 +62,23 @@ class GLock { m_data = static_cast(GlobalLock(m_hmem)); } - ~GLock() { - GlobalUnlock(m_hmem); - } + ~GLock() { GlobalUnlock(m_hmem); } - operator HGLOBAL() { - return m_hmem; - } + operator HGLOBAL() { return m_hmem; } - operator T () { - return m_data; - } + operator T() { return m_data; } - SIZE_T size() { - return GlobalSize(m_hmem); - } + SIZE_T size() { return GlobalSize(m_hmem); } private: HGLOBAL m_hmem; T m_data; }; +} // anonymous namespace + +namespace os { + base::paths DragDataProviderWin::getPaths() { base::paths files; From 9ca5f674f7b9133690bdcb9c004d306013ebe400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 16 Apr 2024 11:12:04 -0300 Subject: [PATCH 14/41] [win] Register the window for Drag and Drop only when the DragTarget is set, and add the possibility to revoke the registration --- os/win/window.cpp | 25 ++++++++++++++++++++----- os/win/window.h | 5 ++++- os/window.cpp | 6 ++++++ os/window.h | 4 +++- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/os/win/window.cpp b/os/win/window.cpp index 67d8de816..10341a0cf 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -277,11 +277,6 @@ WindowWin::WindowWin(const WindowSpec& spec) // WindowSpec::activate() flag for this) m_activate = (spec.parent() == nullptr); - HRESULT result = RegisterDragDrop(m_hwnd, reinterpret_cast(&m_dragTargetAdapter)); - if (result != S_OK) { - LOG(LogLevel::ERROR, "Could not register window for Drag & Drop: %d\n", result); - } - // Log information about the system (for debugging/user support // purposes in case the window doesn't display anything) if (base::get_log_level() >= INFO) { @@ -616,6 +611,26 @@ void WindowWin::setMousePosition(const gfx::Point& position) system()->_setInternalMousePosition(gfx::Point(pos.x, pos.y)); } +void WindowWin::onSetDragTarget() +{ + HRESULT result; + // Drag target was set, then register for drag and drop. + if (hasDragTarget()) { + m_dragTargetAdapter = std::make_unique(this); + result = RegisterDragDrop(m_hwnd, reinterpret_cast(m_dragTargetAdapter.get())); + if (result != S_OK) { + LOG(LogLevel::ERROR, "Could not register window for Drag & Drop: %d\n", result); + } + } + else { // Drag target was unset (set to nullptr), then revoke DnD registration. + m_dragTargetAdapter = nullptr; + result = RevokeDragDrop(m_hwnd); + if (result != S_OK) { + LOG(LogLevel::ERROR, "Could not revoke Drag & Drop: %d\n", result); + } + } +} + bool WindowWin::setCursor(NativeCursor cursor) { HCURSOR hcursor = NULL; diff --git a/os/win/window.h b/os/win/window.h index a5f0f42cd..3efd125b6 100644 --- a/os/win/window.h +++ b/os/win/window.h @@ -73,6 +73,9 @@ namespace os { NativeHandle nativeHandle() const override { return m_hwnd; } + protected: + void onSetDragTarget() override; + private: bool setCursor(HCURSOR hcursor, const CursorRef& cursor); @@ -139,7 +142,7 @@ namespace os { gfx::Rect m_restoredFrame; #endif - DragTargetAdapter m_dragTargetAdapter = DragTargetAdapter(this); + std::unique_ptr m_dragTargetAdapter = nullptr; int m_scale; bool m_isCreated; diff --git a/os/window.cpp b/os/window.cpp index d84a704c6..17bef1dea 100644 --- a/os/window.cpp +++ b/os/window.cpp @@ -48,6 +48,12 @@ void Window::queueEvent(os::Event& ev) onQueueEvent(ev); } +void Window::setDragTarget(DragTarget* delegate) +{ + m_dragTarget = delegate; + onSetDragTarget(); +} + void Window::notifyDragEnter(os::DragEvent& ev) { onDragEnter(ev); diff --git a/os/window.h b/os/window.h index 829f63ba2..b43780891 100644 --- a/os/window.h +++ b/os/window.h @@ -195,7 +195,7 @@ namespace os { // devices like stylus can be used to move and resize the window. std::function handleHitTest = nullptr; - void setDragTarget(DragTarget* delegate) { m_dragTarget = delegate; } + void setDragTarget(DragTarget* delegate); bool hasDragTarget() { return m_dragTarget != nullptr; } template @@ -220,6 +220,8 @@ namespace os { virtual void onDragLeave(os::DragEvent& ev); virtual void onDrop(os::DragEvent& ev); + virtual void onSetDragTarget() {} + private: void* m_userData; DragTarget* m_dragTarget = nullptr; From 050bf56ca47c48e9b50cc243c500cc20077319b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 16 Apr 2024 16:48:36 -0300 Subject: [PATCH 15/41] [win] Refactor code to simplify IDataObject usage --- os/win/dnd.cpp | 176 +++++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 78 deletions(-) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 4f7147279..65124a54d 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -58,21 +58,72 @@ class GLock { public: GLock() = delete; GLock(const GLock&) = delete; - GLock(HGLOBAL hglobal) : m_hmem(hglobal) { + explicit GLock(HGLOBAL hglobal) : m_hmem(hglobal) { m_data = static_cast(GlobalLock(m_hmem)); } - ~GLock() { GlobalUnlock(m_hmem); } + virtual ~GLock() { GlobalUnlock(m_hmem); } operator HGLOBAL() { return m_hmem; } operator T() { return m_data; } + bool operator==(std::nullptr_t) const { return m_data == nullptr; } + bool operator!=(std::nullptr_t) const { return m_data != nullptr; } + SIZE_T size() { return GlobalSize(m_hmem); } private: - HGLOBAL m_hmem; - T m_data; + HGLOBAL m_hmem = nullptr; + T m_data = nullptr; +}; + +// STGMEDIUM wrapper. This is a specific wrapper for the case when the medium +// is of TYMED_HGLOBAL type. Maybe it could be generalized to support the +// other types of mediums, but this is not needed right now, so I'm leaving +// this as is. +template +class Medium : public GLock { +public: + Medium() = delete; + Medium(const Medium&) = delete; + Medium(std::nullptr_t) : GLock(nullptr) { + std::memset(&m_medium, 0, sizeof(STGMEDIUM)); + } + explicit Medium(const STGMEDIUM& medium) : GLock(medium.hGlobal) { + m_medium = medium; + } + + ~Medium() override { ReleaseStgMedium(&m_medium); } + +private: + STGMEDIUM m_medium; +}; + +// IDataObject wrapper. +class DataWrapper { +public: + DataWrapper() = delete; + DataWrapper(const DataWrapper&) = delete; + DataWrapper(IDataObject* data) : m_data(data) {} + + template + Medium get(CLIPFORMAT cfmt) { + STGMEDIUM medium; + FORMATETC fmt; + fmt.cfFormat = cfmt; + fmt.ptd = nullptr; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED::TYMED_HGLOBAL; + if (m_data->GetData(&fmt, &medium) != S_OK) + return nullptr; + + return Medium(medium); + } + +private: + IDataObject* m_data = nullptr; }; } // anonymous namespace @@ -82,31 +133,20 @@ namespace os { base::paths DragDataProviderWin::getPaths() { base::paths files; - FORMATETC fmt; - fmt.cfFormat = CF_HDROP; - fmt.ptd = nullptr; - fmt.dwAspect = DVASPECT_CONTENT; - fmt.lindex = -1; - fmt.tymed = TYMED::TYMED_HGLOBAL; - STGMEDIUM medium; - if (m_data->GetData(&fmt, &medium) == S_OK) { - { - GLock hdrop(medium.hGlobal); - if (static_cast(hdrop)) { - int count = DragQueryFile(hdrop, 0xFFFFFFFF, nullptr, 0); - for (int index = 0; index < count; ++index) { - int length = DragQueryFile(hdrop, index, nullptr, 0); - if (length > 0) { - // From Win32 docs: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew - // the DragQueryFile() does't include the null character in its return value. - std::vector str(length + 1); - DragQueryFile(hdrop, index, str.data(), str.size()); - files.push_back(base::to_utf8(str.data())); - } - } + DataWrapper data(m_data); + Medium hdrop = data.get(CF_HDROP); + if (hdrop != nullptr) { + int count = DragQueryFile(hdrop, 0xFFFFFFFF, nullptr, 0); + for (int index = 0; index < count; ++index) { + int length = DragQueryFile(hdrop, index, nullptr, 0); + if (length > 0) { + // From Win32 docs: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew + // the DragQueryFile() doesn't include the null character in its return value. + std::vector str(length + 1); + DragQueryFile(hdrop, index, str.data(), str.size()); + files.push_back(base::to_utf8(str.data())); } } - ReleaseStgMedium(&medium); } return files; } @@ -114,69 +154,49 @@ base::paths DragDataProviderWin::getPaths() SurfaceRef DragDataProviderWin::getImage() { SurfaceRef surface = nullptr; - clip::image_spec spec; clip::image img; - SurfaceFormatData sfd; - - STGMEDIUM medium; - medium.hGlobal = nullptr; - FORMATETC fmt; - fmt.ptd = nullptr; - fmt.dwAspect = DVASPECT_CONTENT; - fmt.lindex = -1; - fmt.tymed = TYMED_HGLOBAL; + auto makeSurface = [](const clip::image& img) { + SurfaceFormatData sfd; + clip::image_spec spec = img.spec(); + sfd.bitsPerPixel = spec.bits_per_pixel; + sfd.redMask = spec.red_mask; + sfd.greenMask = spec.green_mask; + sfd.blueMask = spec.blue_mask; + sfd.alphaMask = spec.alpha_mask; + sfd.redShift = spec.red_shift; + sfd.greenShift = spec.green_shift; + sfd.blueShift = spec.blue_shift; + sfd.alphaShift = spec.alpha_shift; + sfd.pixelAlpha = PixelAlpha::kStraight; + return os::instance()->makeSurface(spec.width, spec.height, sfd, (unsigned char*)img.data()); + }; + + DataWrapper data(m_data); UINT png_format = RegisterClipboardFormatA("PNG"); if (png_format) { - fmt.cfFormat = png_format; - if (m_data->GetData(&fmt, &medium) == S_OK) { - GLock png_handle(medium.hGlobal); - if (clip::win::read_png(png_handle, png_handle.size(), &img, nullptr)) { - spec = img.spec(); - goto makeSurface; - } - } + Medium png_handle = data.get(png_format); + if (png_handle != nullptr && + clip::win::read_png(png_handle, png_handle.size(), &img, nullptr)) + return makeSurface(img); } - fmt.cfFormat = CF_DIBV5; - if (m_data->GetData(&fmt, &medium) == S_OK) { - GLock b5(medium.hGlobal); + Medium b5 = data.get(CF_DIBV5); + if (b5 != nullptr) { clip::win::BitmapInfo bi(b5); - if (bi.to_image(img)) { - spec = img.spec(); - goto makeSurface; - } + if (bi.to_image(img)) + return makeSurface(img); } - fmt.cfFormat = CF_DIB; - if (m_data->GetData(&fmt, &medium) == S_OK) { - GLock hbi(medium.hGlobal); + Medium hbi = data.get(CF_DIB); + if (hbi != nullptr) { clip::win::BitmapInfo bi(hbi); - if (bi.to_image(img)) { - spec = img.spec(); - goto makeSurface; - } + if (bi.to_image(img)) + return makeSurface(img); } - // No suitable image format found, release medium and return. - goto releaseMedium; - -makeSurface: - sfd.bitsPerPixel = spec.bits_per_pixel; - sfd.redMask = spec.red_mask; - sfd.greenMask = spec.green_mask; - sfd.blueMask = spec.blue_mask; - sfd.alphaMask = spec.alpha_mask; - sfd.redShift = spec.red_shift; - sfd.greenShift = spec.green_shift; - sfd.blueShift = spec.blue_shift; - sfd.alphaShift = spec.alpha_shift; - sfd.pixelAlpha = PixelAlpha::kStraight; - surface = os::instance()->makeSurface(spec.width, spec.height, sfd, (unsigned char*)img.data()); - -releaseMedium: - ReleaseStgMedium(&medium); - return surface; + // No suitable image format found. + return nullptr; } bool DragDataProviderWin::contains(DragDataItemType type) From 63225b3cdea06b1d5b2645288906a2ceedd0006c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 23 Apr 2024 14:30:47 -0300 Subject: [PATCH 16/41] Replace SkiaSystem::makeSurface(int, int, os::SurfaceFormatData&, unsigned char*) by CommonSystem::makeSurface(clip::image&) --- os/common/system.h | 74 ++++++++++++++++++++++++++++++++++++++++ os/none/os.cpp | 6 ++-- os/osx/dnd.mm | 17 +-------- os/skia/skia_surface.cpp | 34 ------------------ os/skia/skia_surface.h | 1 - os/skia/skia_system.h | 7 ---- os/system.h | 10 +++++- os/win/dnd.cpp | 22 ++---------- 8 files changed, 90 insertions(+), 81 deletions(-) diff --git a/os/common/system.h b/os/common/system.h index 4de59e473..01c26a925 100644 --- a/os/common/system.h +++ b/os/common/system.h @@ -30,6 +30,10 @@ #include "os/menus.h" #include "os/system.h" +#if CLIP_ENABLE_IMAGE +#include "clip/clip.h" +#endif + #include namespace os { @@ -120,6 +124,76 @@ class CommonSystem : public System { isKeyPressed(kKeyRWin) ? kKeyWinModifier: 0)); } +#if CLIP_ENABLE_IMAGE + + SurfaceRef makeSurface(const clip::image& image) override + { + const clip::image_spec spec = image.spec(); + + if (spec.bits_per_pixel != 32 && + spec.bits_per_pixel != 24 && + spec.bits_per_pixel != 16) + return nullptr; + + SurfaceRef surface = ((System*)this)->makeRgbaSurface(spec.width, spec.height); + SurfaceFormatData sfd; + surface->getFormat(&sfd); + + auto get_rgba32 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { + uint32_t c = *((uint32_t*)scanlineAddr); + *r = ((c & spec.red_mask) >> spec.red_shift); + *g = ((c & spec.green_mask)>> spec.green_shift); + *b = ((c & spec.blue_mask) >> spec.blue_shift); + *a = ((c & spec.alpha_mask)>> spec.alpha_shift); + }; + auto get_rgba24 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { + uint32_t c = *((uint32_t*)scanlineAddr); + *r = ((c & spec.red_mask) >> spec.red_shift); + *g = ((c & spec.green_mask)>> spec.green_shift); + *b = ((c & spec.blue_mask) >> spec.blue_shift); + *a = 255; + }; + auto get_rgba16 = [&spec](char* scanlineAddr, int* r, int* g, int *b, int *a) { + uint16_t c = *((uint16_t*)scanlineAddr); + *r = (((c & spec.red_mask )>>spec.red_shift )*255) / (spec.red_mask >>spec.red_shift); + *g = (((c & spec.green_mask)>>spec.green_shift)*255) / (spec.green_mask>>spec.green_shift); + *b = (((c & spec.blue_mask )>>spec.blue_shift )*255) / (spec.blue_mask >>spec.blue_shift); + *a = 255; + }; + + // Select color components retrieval function. + std::function get_rgba; + switch (spec.bits_per_pixel) { + case 32: + get_rgba = get_rgba32; + break; + case 24: + get_rgba = get_rgba24; + break; + case 16: + get_rgba = get_rgba16; + break; + } + + for (int v=0; vgetData(0, v); + char* src = image.data() + v * spec.bytes_per_row; + for (int u=0; u makeWindow(const WindowSpec& spec) override { return nullptr; } Ref makeSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { return nullptr; } - Ref makeSurface(int width, int height, - const os::SurfaceFormatData& sf, - const unsigned char* data = nullptr) override { return nullptr; } +#if CLIP_ENABLE_IMAGE + Ref makeSurface(const clip::image& image) override { return nullptr; } +#endif Ref makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { return nullptr; } Ref loadSurface(const char* filename) override { return nullptr; } diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm index 37def047c..16dafee02 100644 --- a/os/osx/dnd.mm +++ b/os/osx/dnd.mm @@ -40,22 +40,7 @@ if (!clip::osx::get_image_from_clipboard(m_pasteboard, &img, &spec)) return nullptr; - os::SurfaceFormatData sfd; - sfd.bitsPerPixel = spec.bits_per_pixel; - sfd.redMask = spec.red_mask; - sfd.greenMask = spec.green_mask; - sfd.blueMask = spec.blue_mask; - sfd.alphaMask = spec.alpha_mask; - sfd.redShift = spec.red_shift; - sfd.greenShift = spec.green_shift; - sfd.blueShift = spec.blue_shift; - sfd.alphaShift = spec.alpha_shift; - sfd.pixelAlpha = PixelAlpha::kStraight; - - SurfaceRef surface = os::instance()->makeSurface( - spec.width, spec.height, - sfd, (unsigned char*) img.data()); - return surface; + return os::instance()->makeSurface(img); } bool DragDataProviderOSX::contains(DragDataItemType type) diff --git a/os/skia/skia_surface.cpp b/os/skia/skia_surface.cpp index f461bbd7a..c963edea0 100644 --- a/os/skia/skia_surface.cpp +++ b/os/skia/skia_surface.cpp @@ -82,40 +82,6 @@ void SkiaSurface::create(int width, int height, const os::ColorSpaceRef& cs) swapBitmap(bmp); } -void SkiaSurface::create(int width, int height, - const os::SurfaceFormatData& sf, - const unsigned char* data) -{ - destroy(); - - ASSERT(!m_surface) - ASSERT(width > 0); - ASSERT(height > 0); - - SkColorType ct = SkiaSurface::deductSkColorType(sf); - SkAlphaType at = SkiaSurface::asSkAlphaType(sf.pixelAlpha); - - SkBitmap bmp; - if (!bmp.tryAllocPixels( - SkImageInfo::Make(width, height, ct, at))) - throw base::Exception("Cannot create Skia surface"); - - if (data) { - // Convert 24bpp data to 32bpp data because Skia doesn't work with 24bpp - // images. - if (sf.bitsPerPixel == 24) { - for (int i=0,j=0; j(bmp.getPixels())[i], &data[j], 3); - } - else - bmp.writePixels(SkPixmap(bmp.info(), data, size_t(width) * size_t(sf.bitsPerPixel / 8))); - } - else { - bmp.eraseColor(SK_ColorTRANSPARENT); - } - swapBitmap(bmp); -} - void SkiaSurface::createRgba(int width, int height, const os::ColorSpaceRef& cs) { destroy(); diff --git a/os/skia/skia_surface.h b/os/skia/skia_surface.h index 2ec52182a..ee6142866 100644 --- a/os/skia/skia_surface.h +++ b/os/skia/skia_surface.h @@ -33,7 +33,6 @@ class SkiaSurface final : public Surface { ~SkiaSurface(); void create(int width, int height, const os::ColorSpaceRef& cs); - void create(int width, int height, const SurfaceFormatData& sf, const unsigned char* data); void createRgba(int width, int height, const os::ColorSpaceRef& cs); void destroy(); diff --git a/os/skia/skia_system.h b/os/skia/skia_system.h index 8e85c6ce8..557692d16 100644 --- a/os/skia/skia_system.h +++ b/os/skia/skia_system.h @@ -104,13 +104,6 @@ class SkiaSystem final : public SkiaSystemBase { return sur; } - SurfaceRef makeSurface(int width, int height, - const os::SurfaceFormatData& sf, - const unsigned char* data) override { - auto sur = make_ref(); - sur->create(width, height, sf, data); - return sur; - } SurfaceRef makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { diff --git a/os/system.h b/os/system.h index 3b46ce78d..bbcfdb45f 100644 --- a/os/system.h +++ b/os/system.h @@ -24,6 +24,12 @@ #include #include +#if CLIP_ENABLE_IMAGE +namespace clip { + class image; +} +#endif + namespace os { class ColorSpaceConversion; @@ -162,7 +168,9 @@ namespace os { } virtual Ref makeSurface(int width, int height, const os::ColorSpaceRef& colorSpace = nullptr) = 0; - virtual Ref makeSurface(int width, int height, const os::SurfaceFormatData& sf, const unsigned char* data = nullptr) = 0; +#if CLIP_ENABLE_IMAGE + virtual Ref makeSurface(const clip::image& image) = 0; +#endif virtual Ref makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace = nullptr) = 0; virtual Ref loadSurface(const char* filename) = 0; virtual Ref loadRgbaSurface(const char* filename) = 0; diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 65124a54d..b3c86e557 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -156,43 +156,27 @@ SurfaceRef DragDataProviderWin::getImage() SurfaceRef surface = nullptr; clip::image img; - auto makeSurface = [](const clip::image& img) { - SurfaceFormatData sfd; - clip::image_spec spec = img.spec(); - sfd.bitsPerPixel = spec.bits_per_pixel; - sfd.redMask = spec.red_mask; - sfd.greenMask = spec.green_mask; - sfd.blueMask = spec.blue_mask; - sfd.alphaMask = spec.alpha_mask; - sfd.redShift = spec.red_shift; - sfd.greenShift = spec.green_shift; - sfd.blueShift = spec.blue_shift; - sfd.alphaShift = spec.alpha_shift; - sfd.pixelAlpha = PixelAlpha::kStraight; - return os::instance()->makeSurface(spec.width, spec.height, sfd, (unsigned char*)img.data()); - }; - DataWrapper data(m_data); UINT png_format = RegisterClipboardFormatA("PNG"); if (png_format) { Medium png_handle = data.get(png_format); if (png_handle != nullptr && clip::win::read_png(png_handle, png_handle.size(), &img, nullptr)) - return makeSurface(img); + return os::instance()->makeSurface(img); } Medium b5 = data.get(CF_DIBV5); if (b5 != nullptr) { clip::win::BitmapInfo bi(b5); if (bi.to_image(img)) - return makeSurface(img); + return os::instance()->makeSurface(img); } Medium hbi = data.get(CF_DIB); if (hbi != nullptr) { clip::win::BitmapInfo bi(hbi); if (bi.to_image(img)) - return makeSurface(img); + return os::instance()->makeSurface(img); } // No suitable image format found. From 071ddb94588eb09d84e079be3111f76bb39a23db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 26 Apr 2024 18:00:40 -0300 Subject: [PATCH 17/41] Add support to drag and drop URLs --- examples/drag_and_drop.cpp | 16 ++++++++++++++-- os/dnd.h | 2 ++ os/win/dnd.cpp | 29 +++++++++++++++++++++++++---- os/win/dnd.h | 1 + 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index 95b9420dd..c647b1eba 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -31,6 +31,7 @@ struct WindowData { int drag; base::paths paths; os::SurfaceRef image; + std::string url; gfx::Point dragPosition; gfx::Rect dropZone; }; @@ -78,6 +79,8 @@ class DragTarget : public os::DragTarget { windowData.paths = ev.dataProvider()->getPaths(); if (ev.dataProvider()->contains(os::DragDataItemType::Image)) windowData.image = ev.dataProvider()->getImage(); + if (ev.dataProvider()->contains(os::DragDataItemType::Url)) + windowData.url = ev.dataProvider()->getUrl(); } redraw_window(ev.target()); @@ -89,12 +92,12 @@ static void redraw_window(os::Window* window) { os::Surface* s = window->surface(); os::Paint paint; - paint.color(gfx::rgba(0, 0, 0)); + paint.color(gfx::rgba(128, 128, 128)); s->drawRect(window->bounds(), paint); paint.color(gfx::rgba(255, 255, 255)); - char buf[256]; + char buf[2049]; int y = 12; std::snprintf(buf, sizeof(buf), "Drag Position = [%d, %d]", windowData.dragPosition.x, windowData.dragPosition.y); os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); @@ -119,6 +122,15 @@ static void redraw_window(os::Window* window) } } + if (!windowData.url.empty()) { + y += 12; + std::snprintf(buf, sizeof(buf), "URL:"); + os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint); + y += 12; + std::snprintf(buf, sizeof(buf), "%s", windowData.url.c_str()); + os::draw_text(s, nullptr, buf, gfx::Point(12, y), &paint); + } + if (windowData.image) { y += 12; s->drawSurface(windowData.image.get(), 0, y); diff --git a/os/dnd.h b/os/dnd.h index e1eb8efdd..22b357e6b 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -36,6 +36,7 @@ namespace os { enum class DragDataItemType { Paths, Image, + Url, }; // Interface to get dragged data from the platform's implementation. @@ -44,6 +45,7 @@ namespace os { virtual ~DragDataProvider() {} virtual base::paths getPaths() = 0; virtual SurfaceRef getImage() = 0; + virtual std::string getUrl() = 0; virtual bool contains(DragDataItemType type) { return false; } }; diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index b3c86e557..82dcbb679 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -183,12 +183,26 @@ SurfaceRef DragDataProviderWin::getImage() return nullptr; } +std::string DragDataProviderWin::getUrl() +{ + DataWrapper data(m_data); + UINT urlFormat = RegisterClipboardFormat(CFSTR_INETURL); + + Medium url = data.get(urlFormat); + if (url == nullptr) + return std::string(); + + return std::string(base::to_utf8((TCHAR*)url)); +} + bool DragDataProviderWin::contains(DragDataItemType type) { base::ComPtr formats; if (m_data->EnumFormatEtc(DATADIR::DATADIR_GET, &formats) != S_OK) return false; + UINT urlFormat = RegisterClipboardFormat(CFSTR_INETURL); + UINT pngFormat = RegisterClipboardFormat(L"PNG"); char name[101]; FORMATETC fmt; while (formats->Next(1, &fmt, nullptr) == S_OK) { @@ -206,10 +220,17 @@ bool DragDataProviderWin::contains(DragDataItemType type) return true; break; default: { - int namelen = GetClipboardFormatNameA(fmt.cfFormat, name, 100); - name[namelen] = '\0'; - if (std::strcmp(name, "PNG") == 0 && type == DragDataItemType::Image) - return true; + switch (type) { + case DragDataItemType::Image: + if (fmt.cfFormat == pngFormat) + return true; + + break; + case DragDataItemType::Url: + if (fmt.cfFormat == urlFormat) + return true; + break; + } break; } } diff --git a/os/win/dnd.h b/os/win/dnd.h index b915ba33b..8311aa4e0 100644 --- a/os/win/dnd.h +++ b/os/win/dnd.h @@ -24,6 +24,7 @@ namespace os { base::paths getPaths() override; SurfaceRef getImage() override; + std::string getUrl() override; bool contains(DragDataItemType type) override; }; From 449e9782fecf46e6c6c27fabb5984900765e03c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 26 Apr 2024 18:04:51 -0300 Subject: [PATCH 18/41] Fix makeSurface(clip::image&) to premultiply the source RGB values by the alpha component --- examples/drag_and_drop.cpp | 2 +- os/common/system.h | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index c647b1eba..083f3cfc4 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -133,7 +133,7 @@ static void redraw_window(os::Window* window) if (windowData.image) { y += 12; - s->drawSurface(windowData.image.get(), 0, y); + s->drawRgbaSurface(windowData.image.get(), 0, y); } paint.style(os::Paint::Style::Stroke); diff --git a/os/common/system.h b/os/common/system.h index 01c26a925..1b5b2b806 100644 --- a/os/common/system.h +++ b/os/common/system.h @@ -141,10 +141,13 @@ class CommonSystem : public System { auto get_rgba32 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { uint32_t c = *((uint32_t*)scanlineAddr); - *r = ((c & spec.red_mask) >> spec.red_shift); - *g = ((c & spec.green_mask)>> spec.green_shift); - *b = ((c & spec.blue_mask) >> spec.blue_shift); - *a = ((c & spec.alpha_mask)>> spec.alpha_shift); + *a = ((c & spec.alpha_mask) >> spec.alpha_shift); + // The source image is in straight-alpha and makeRgbaSurface returns a + // surface using premultiplied-alpha so we have to premultiply the + // source values. + *r = ((c & spec.red_mask) >> spec.red_shift )*(*a)/255; + *g = ((c & spec.green_mask)>> spec.green_shift)*(*a)/255; + *b = ((c & spec.blue_mask) >> spec.blue_shift )*(*a)/255; }; auto get_rgba24 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { uint32_t c = *((uint32_t*)scanlineAddr); From c35366f7faae561bef3eef2937990f647585adda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 29 Apr 2024 10:58:45 -0300 Subject: [PATCH 19/41] [osx] Add support to drag and drop URLs --- os/osx/dnd.h | 2 ++ os/osx/dnd.mm | 11 +++++++++++ os/osx/view.mm | 1 + 3 files changed, 14 insertions(+) diff --git a/os/osx/dnd.h b/os/osx/dnd.h index d284ba9ed..0f2dd85bb 100644 --- a/os/osx/dnd.h +++ b/os/osx/dnd.h @@ -29,6 +29,8 @@ namespace os { SurfaceRef getImage() override; + std::string getUrl() override; + bool contains(DragDataItemType type) override; }; } // namespace os diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm index 16dafee02..067bc707c 100644 --- a/os/osx/dnd.mm +++ b/os/osx/dnd.mm @@ -8,6 +8,7 @@ #include "base/fs.h" #include "clip/clip.h" #include "clip/clip_osx.h" +#include "os/dnd.h" #include "os/osx/dnd.h" #include "os/surface_format.h" #include "os/system.h" @@ -43,6 +44,12 @@ return os::instance()->makeSurface(img); } +std::string DragDataProviderOSX::getUrl() +{ + NSURL* url = [NSURL URLFromPasteboard: m_pasteboard]; + return url ? url.absoluteString.UTF8String : ""; +} + bool DragDataProviderOSX::contains(DragDataItemType type) { for (NSPasteboardType t in m_pasteboard.types) { @@ -54,6 +61,10 @@ ([t isEqual: NSPasteboardTypeTIFF] || [t isEqual: NSPasteboardTypePNG])) return true; + + if (type == DragDataItemType::Url && + [t isEqual: NSURLPboardType]) + return true; } return false; } diff --git a/os/osx/view.mm b/os/osx/view.mm index 0e939b384..7ba35f07e 100644 --- a/os/osx/view.mm +++ b/os/osx/view.mm @@ -150,6 +150,7 @@ - (id)initWithFrame:(NSRect)frameRect NSFilenamesPboardType, NSPasteboardTypePNG, NSPasteboardTypeTIFF, + NSURLPboardType, nil]]; // Create a CALayer for backing content with async drawing. This From 5d44a5221c632d704d525d47d303f5ed095ce9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 3 May 2024 15:23:03 -0300 Subject: [PATCH 20/41] Set destination alpha component value to 255 when converting color from a source image that doesn't support alpha --- os/common/system.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/os/common/system.h b/os/common/system.h index 1b5b2b806..195d69830 100644 --- a/os/common/system.h +++ b/os/common/system.h @@ -141,7 +141,10 @@ class CommonSystem : public System { auto get_rgba32 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { uint32_t c = *((uint32_t*)scanlineAddr); - *a = ((c & spec.alpha_mask) >> spec.alpha_shift); + if (spec.alpha_mask) + *a = ((c & spec.alpha_mask) >> spec.alpha_shift); + else + *a = 255; // The source image is in straight-alpha and makeRgbaSurface returns a // surface using premultiplied-alpha so we have to premultiply the // source values. From d5b99bb1c5faad2e71af667b71577c450fb02dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 3 May 2024 15:39:37 -0300 Subject: [PATCH 21/41] [win] Mimic on Windows the macOS's Chrome drag and drop behavior of images and offer the possibility of configuring the decoding functions to be used This is achieved by looking at the clipboard for a file that contains data in some of the supported image formats, because in Windows, the Chrome implementation puts the image's file content in the clipboard. Then after determining that the file contents corresponds to one of our supported image formats, the configured decoding function is used (which might be the default one) --- clip | 2 +- examples/drag_and_drop.cpp | 2 + os/CMakeLists.txt | 1 + os/dnd.cpp | 103 +++++++++++++++++++++++++++++++++++++ os/dnd.h | 18 +++++++ os/win/dnd.cpp | 64 ++++++++++++++++++++--- os/win/dnd.h | 2 + 7 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 os/dnd.cpp diff --git a/clip b/clip index 6fa80b237..dc8a30a76 160000 --- a/clip +++ b/clip @@ -1 +1 @@ -Subproject commit 6fa80b237e9abcc3828434864d231129205f48f9 +Subproject commit dc8a30a767c1826a40332fb9a3a10307aeeef1e9 diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index 083f3cfc4..32b34d054 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -77,8 +77,10 @@ class DragTarget : public os::DragTarget { if (ev.acceptDrop()) { if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) windowData.paths = ev.dataProvider()->getPaths(); +#if CLIP_ENABLE_IMAGE if (ev.dataProvider()->contains(os::DragDataItemType::Image)) windowData.image = ev.dataProvider()->getImage(); +#endif if (ev.dataProvider()->contains(os::DragDataItemType::Url)) windowData.url = ev.dataProvider()->getUrl(); } diff --git a/os/CMakeLists.txt b/os/CMakeLists.txt index e36f91eaa..67d135a9c 100644 --- a/os/CMakeLists.txt +++ b/os/CMakeLists.txt @@ -8,6 +8,7 @@ set(LAF_OS_SOURCES common/event_queue.cpp common/main.cpp + dnd.cpp system.cpp window.cpp) if(WIN32) diff --git a/os/dnd.cpp b/os/dnd.cpp new file mode 100644 index 000000000..422d6af7a --- /dev/null +++ b/os/dnd.cpp @@ -0,0 +1,103 @@ +// LAF OS Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#if CLIP_ENABLE_IMAGE + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "clip/clip.h" +#include "clip/clip_win.h" +#include "os/dnd.h" +#include "os/system.h" + +namespace os { + +SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_png(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_jpg(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_bmp(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_gif(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +static DecoderFunc g_decode_png = default_decode_png; +static DecoderFunc g_decode_jpg = default_decode_jpg; +static DecoderFunc g_decode_bmp = default_decode_bmp; +static DecoderFunc g_decode_gif = default_decode_gif; + +void set_decode_png(DecoderFunc func) +{ + g_decode_png = func; +} + +void set_decode_jpg(DecoderFunc func) +{ + g_decode_jpg = func; +} + +void set_decode_bmp(DecoderFunc func) +{ + g_decode_bmp = func; +} + +void set_decode_gif(DecoderFunc func) +{ + g_decode_gif = func; +} + +SurfaceRef decode_png(const uint8_t* buf, const uint32_t len) +{ + return g_decode_png(buf, len); +} + +SurfaceRef decode_jpg(const uint8_t* buf, const uint32_t len) +{ + return g_decode_jpg(buf, len); +} + +SurfaceRef decode_bmp(const uint8_t* buf, const uint32_t len) +{ + return g_decode_bmp(buf, len); +} + +SurfaceRef decode_gif(const uint8_t* buf, const uint32_t len) +{ + return g_decode_gif(buf, len); +} + +} // namespace os + +#endif \ No newline at end of file diff --git a/os/dnd.h b/os/dnd.h index 22b357e6b..734a312f7 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -13,6 +13,7 @@ #include "gfx/point.h" #include "os/surface.h" +#include #include #pragma push_macro("None") @@ -22,6 +23,21 @@ namespace os { class Window; +#if CLIP_ENABLE_IMAGE + using DecoderFunc = std::function; + + // Methods used to configure custom decoder functions by replacing the default implementations. + + void set_decode_png(DecoderFunc func); + void set_decode_jpg(DecoderFunc func); + void set_decode_bmp(DecoderFunc func); + void set_decode_gif(DecoderFunc func); + + SurfaceRef decode_png(const uint8_t* buf, const uint32_t len); + SurfaceRef decode_jpg(const uint8_t* buf, const uint32_t len); + SurfaceRef decode_bmp(const uint8_t* buf, const uint32_t len); + SurfaceRef decode_gif(const uint8_t* buf, const uint32_t len); +#endif // Operations that can be supported by source and target windows in a drag // and drop operation. enum class DropOperation { @@ -44,7 +60,9 @@ namespace os { public: virtual ~DragDataProvider() {} virtual base::paths getPaths() = 0; +#if CLIP_ENABLE_IMAGE virtual SurfaceRef getImage() = 0; +#endif virtual std::string getUrl() = 0; virtual bool contains(DragDataItemType type) { return false; } }; diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 82dcbb679..e52f51382 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -4,6 +4,7 @@ // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. +#include "base/fs.h" #include "os/window.h" #include "os/win/dnd.h" #include "os/system.h" @@ -68,6 +69,8 @@ class GLock { operator T() { return m_data; } + T operator ->() { return m_data; } + bool operator==(std::nullptr_t) const { return m_data == nullptr; } bool operator!=(std::nullptr_t) const { return m_data != nullptr; } @@ -108,13 +111,14 @@ class DataWrapper { DataWrapper(IDataObject* data) : m_data(data) {} template - Medium get(CLIPFORMAT cfmt) { + Medium get(CLIPFORMAT cfmt, LONG lindex = -1) + { STGMEDIUM medium; FORMATETC fmt; fmt.cfFormat = cfmt; fmt.ptd = nullptr; fmt.dwAspect = DVASPECT_CONTENT; - fmt.lindex = -1; + fmt.lindex = lindex; fmt.tymed = TYMED::TYMED_HGLOBAL; if (m_data->GetData(&fmt, &medium) != S_OK) return nullptr; @@ -151,20 +155,20 @@ base::paths DragDataProviderWin::getPaths() return files; } +#if CLIP_ENABLE_IMAGE SurfaceRef DragDataProviderWin::getImage() { SurfaceRef surface = nullptr; - clip::image img; DataWrapper data(m_data); UINT png_format = RegisterClipboardFormatA("PNG"); if (png_format) { Medium png_handle = data.get(png_format); - if (png_handle != nullptr && - clip::win::read_png(png_handle, png_handle.size(), &img, nullptr)) - return os::instance()->makeSurface(img); + if (png_handle != nullptr) + return os::decode_png(png_handle, png_handle.size()); } + clip::image img; Medium b5 = data.get(CF_DIBV5); if (b5 != nullptr) { clip::win::BitmapInfo bi(b5); @@ -179,9 +183,40 @@ SurfaceRef DragDataProviderWin::getImage() return os::instance()->makeSurface(img); } + // If there is a file descriptor available, then we inspect the first + // file of the group to see if its filename has any of the supported + // filename extensions for image formats. + UINT fileDescriptorFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); + UINT fileContentsFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS); + if (fileDescriptorFormat) { + Medium fgd = data.get(fileDescriptorFormat); + if (fgd != nullptr && fgd->cItems > 0) { + // Get content of the first file on the group. + Medium content = data.get(fileContentsFormat, 0); + if (content != nullptr) { + std::string filename(base::to_utf8(fgd->fgd->cFileName)); + std::string ext = base::get_file_extension(filename); + std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); + + if (ext == "PNG") + return os::decode_png(content, content.size()); + + if (ext == "JPG" || ext == "JPEG" || ext == "JPE") + return os::decode_jpg(content, content.size()); + + if (ext == "GIF") + return os::decode_gif(content, content.size()); + + if (ext == "BMP") + return os::decode_bmp(content, content.size()); + } + } + } + // No suitable image format found. return nullptr; } +#endif std::string DragDataProviderWin::getUrl() { @@ -203,6 +238,7 @@ bool DragDataProviderWin::contains(DragDataItemType type) UINT urlFormat = RegisterClipboardFormat(CFSTR_INETURL); UINT pngFormat = RegisterClipboardFormat(L"PNG"); + UINT fileDescriptorFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); char name[101]; FORMATETC fmt; while (formats->Next(1, &fmt, nullptr) == S_OK) { @@ -211,6 +247,7 @@ bool DragDataProviderWin::contains(DragDataItemType type) if (type == DragDataItemType::Paths) return true; break; +#if CLIP_ENABLE_IMAGE case CF_DIBV5: if (type == DragDataItemType::Image) return true; @@ -219,13 +256,28 @@ bool DragDataProviderWin::contains(DragDataItemType type) if (type == DragDataItemType::Image) return true; break; +#endif default: { switch (type) { +#if CLIP_ENABLE_IMAGE case DragDataItemType::Image: if (fmt.cfFormat == pngFormat) return true; + if (fmt.cfFormat == fileDescriptorFormat) { + DataWrapper data(m_data); + Medium fgd = data.get(fileDescriptorFormat); + if (fgd != nullptr && fgd->cItems > 0) { + std::string filename(base::to_utf8(fgd->fgd->cFileName)); + std::string ext = base::get_file_extension(filename); + std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); + if (ext == "PNG" || ext == "JPG" || ext == "JPEG" || + ext == "JPE" || ext == "GIF" || ext == "BMP") + return true; + } + } break; +#endif case DragDataItemType::Url: if (fmt.cfFormat == urlFormat) return true; diff --git a/os/win/dnd.h b/os/win/dnd.h index 8311aa4e0..7834e8232 100644 --- a/os/win/dnd.h +++ b/os/win/dnd.h @@ -23,7 +23,9 @@ namespace os { IDataObject* m_data; base::paths getPaths() override; +#if CLIP_ENABLE_IMAGE SurfaceRef getImage() override; +#endif std::string getUrl() override; bool contains(DragDataItemType type) override; }; From 54835683fc54bab04c52fe36ecbecdcbd3b531d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 3 May 2024 17:44:23 -0300 Subject: [PATCH 22/41] Fix function declarations and definitions to separate windows impl details from the public interface --- os/dnd.cpp | 48 +++++------------------------------------------- os/dnd.h | 5 +++++ os/win/dnd.cpp | 42 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/os/dnd.cpp b/os/dnd.cpp index 422d6af7a..384aea4ad 100644 --- a/os/dnd.cpp +++ b/os/dnd.cpp @@ -10,53 +10,15 @@ #include "config.h" #endif -#include "clip/clip.h" -#include "clip/clip_win.h" #include "os/dnd.h" #include "os/system.h" -namespace os { - -SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len) -{ - clip::image img; - if (!clip::win::read_png(buf, len, &img, nullptr)) - return nullptr; - - return os::instance()->makeSurface(img); -} - -SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len) -{ - clip::image img; - if (!clip::win::read_jpg(buf, len, &img, nullptr)) - return nullptr; - - return os::instance()->makeSurface(img); -} +static os::DecoderFunc g_decode_png = default_decode_png; +static os::DecoderFunc g_decode_jpg = default_decode_jpg; +static os::DecoderFunc g_decode_bmp = default_decode_bmp; +static os::DecoderFunc g_decode_gif = default_decode_gif; -SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len) -{ - clip::image img; - if (!clip::win::read_bmp(buf, len, &img, nullptr)) - return nullptr; - - return os::instance()->makeSurface(img); -} - -SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len) -{ - clip::image img; - if (!clip::win::read_gif(buf, len, &img, nullptr)) - return nullptr; - - return os::instance()->makeSurface(img); -} - -static DecoderFunc g_decode_png = default_decode_png; -static DecoderFunc g_decode_jpg = default_decode_jpg; -static DecoderFunc g_decode_bmp = default_decode_bmp; -static DecoderFunc g_decode_gif = default_decode_gif; +namespace os { void set_decode_png(DecoderFunc func) { diff --git a/os/dnd.h b/os/dnd.h index 734a312f7..0c80a8713 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -19,6 +19,11 @@ #pragma push_macro("None") #undef None // Undefine the X11 None macro +os::SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len); +os::SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len); +os::SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len); +os::SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len); + namespace os { class Window; diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index e52f51382..c00dc18f2 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -132,6 +132,44 @@ class DataWrapper { } // anonymous namespace +#if CLIP_ENABLE_IMAGE +os::SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_png(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +os::SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_jpg(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +os::SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_bmp(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} + +os::SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len) +{ + clip::image img; + if (!clip::win::read_gif(buf, len, &img, nullptr)) + return nullptr; + + return os::instance()->makeSurface(img); +} +#endif + namespace os { base::paths DragDataProviderWin::getPaths() @@ -268,8 +306,8 @@ bool DragDataProviderWin::contains(DragDataItemType type) DataWrapper data(m_data); Medium fgd = data.get(fileDescriptorFormat); if (fgd != nullptr && fgd->cItems > 0) { - std::string filename(base::to_utf8(fgd->fgd->cFileName)); - std::string ext = base::get_file_extension(filename); + const std::string filename(base::to_utf8(fgd->fgd->cFileName)); + std::string ext = base::get_file_extension(filename); std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); if (ext == "PNG" || ext == "JPG" || ext == "JPEG" || ext == "JPE" || ext == "GIF" || ext == "BMP") From 5cf80a0d6a3e0cf2d73bd513f811172131585cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 6 May 2024 14:11:30 -0300 Subject: [PATCH 23/41] Update clip submodule --- clip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clip b/clip index dc8a30a76..7fa83b2f8 160000 --- a/clip +++ b/clip @@ -1 +1 @@ -Subproject commit dc8a30a767c1826a40332fb9a3a10307aeeef1e9 +Subproject commit 7fa83b2f817a7da49e8ea27f6dbe2775ead13ddf From a522a9ee4c37372183b52df73ded18f98abc1744 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 7 May 2024 11:17:45 -0300 Subject: [PATCH 24/41] Change colors of drag_and_drop example --- examples/drag_and_drop.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index 32b34d054..77ad79aa7 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -94,10 +94,10 @@ static void redraw_window(os::Window* window) { os::Surface* s = window->surface(); os::Paint paint; - paint.color(gfx::rgba(128, 128, 128)); + paint.color(gfx::rgba(32, 32, 32, 255)); s->drawRect(window->bounds(), paint); - paint.color(gfx::rgba(255, 255, 255)); + paint.color(gfx::rgba(255, 255, 200, 255)); char buf[2049]; int y = 12; @@ -142,7 +142,7 @@ static void redraw_window(os::Window* window) s->drawRect(window->bounds(), paint); - auto zoneColor = gfx::rgba(100, 255, 100); + auto zoneColor = paint.color(); auto textColor = zoneColor; windowData.dropZone.x = window->width() - windowData.dropZone.w - 12; if (windowData.dropZone.contains(windowData.dragPosition)){ From a5f0f0f0a8938d57b35010536d1a7a0bc80db5ca Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 7 May 2024 11:34:36 -0300 Subject: [PATCH 25/41] Invalidate window inside draw_window() in drag_and_drop example This is how most examples are working right now, draw and invalidate the updated area in the same function just to avoid calling invalidate() each time. --- examples/drag_and_drop.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index 77ad79aa7..27c7f8c8e 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -53,20 +53,17 @@ class DragTarget : public os::DragTarget { windowData.drag = 0; windowData.dragPosition = ev.position(); redraw_window(ev.target()); - ev.target()->invalidate(); } void dragLeave(os::DragEvent& ev) override { windowData.dragEnter = false; windowData.dragLeave = true; windowData.dragPosition = ev.position(); redraw_window(ev.target()); - ev.target()->invalidate(); } void drag(os::DragEvent& ev) override { ++windowData.drag; windowData.dragPosition = ev.position(); redraw_window(ev.target()); - ev.target()->invalidate(); } void drop(os::DragEvent& ev) override { windowData.dragEnter = false; @@ -86,7 +83,6 @@ class DragTarget : public os::DragTarget { } redraw_window(ev.target()); - ev.target()->invalidate(); } }; @@ -159,6 +155,11 @@ static void redraw_window(os::Window* window) paint.color(textColor); paint.style(os::Paint::Style::Fill); os::draw_text(s, nullptr, "Drop here!", windowData.dropZone.center(), &paint, os::TextAlign::Center); + + if (window->isVisible()) + window->invalidateRegion(gfx::Region(rc)); + else + window->setVisible(true); } static os::WindowRef create_window(const std::string& title, @@ -210,7 +211,6 @@ int app_main(int argc, char* argv[]) case os::Event::ResizeWindow: redraw_window(ev.window().get()); - ev.window()->invalidate(); break; default: From 76d69675b7b188fc73259bee7cbd62ab11b70fa9 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 7 May 2024 11:46:21 -0300 Subject: [PATCH 26/41] Add scale=2 to drag_and_drop example to test the DnD logic w/scale We need drag & drop working with scale parameter (as Aseprite uses scale=2 by default), so I've made some changes to test: 1) draw_window() must use the surface bounds which are scaled (not the windows bounds, which aren't) 2) The drop zone must use the same scaled bounds 3) Moved all logic to create the window inside create_window() --- examples/drag_and_drop.cpp | 39 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index 27c7f8c8e..cdfa16c76 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -89,9 +89,11 @@ class DragTarget : public os::DragTarget { static void redraw_window(os::Window* window) { os::Surface* s = window->surface(); + const gfx::Rect rc = s->bounds(); + os::Paint paint; paint.color(gfx::rgba(32, 32, 32, 255)); - s->drawRect(window->bounds(), paint); + s->drawRect(rc, paint); paint.color(gfx::rgba(255, 255, 200, 255)); @@ -135,13 +137,13 @@ static void redraw_window(os::Window* window) } paint.style(os::Paint::Style::Stroke); - s->drawRect(window->bounds(), paint); - + s->drawRect(rc, paint); auto zoneColor = paint.color(); auto textColor = zoneColor; - windowData.dropZone.x = window->width() - windowData.dropZone.w - 12; - if (windowData.dropZone.contains(windowData.dragPosition)){ + + windowData.dropZone.x = rc.w - windowData.dropZone.w - 12; + if (windowData.dropZone.contains(windowData.dragPosition)) { paint.style(os::Paint::Style::Fill); paint.color(zoneColor); s->drawRect(windowData.dropZone, paint); @@ -162,16 +164,22 @@ static void redraw_window(os::Window* window) window->setVisible(true); } -static os::WindowRef create_window(const std::string& title, - const os::WindowSpec& spec, - os::DragTarget& dragTarget) +static os::WindowRef create_window(os::DragTarget& dragTarget) { + auto screen = os::instance()->mainScreen(); + os::WindowSpec spec; + spec.titled(true); + spec.position(os::WindowSpec::Position::Frame); + spec.frame(screen->workarea()/2); + spec.screen(screen); + spec.scale(2); + os::WindowRef newWindow = os::instance()->makeWindow(spec); newWindow->setCursor(os::NativeCursor::Arrow); - newWindow->setTitle(title); - newWindow->setVisible(true); + newWindow->setTitle("Drag & Drop example"); newWindow->setDragTarget(&dragTarget); - windowData.dropZone = {spec.frame().w - 64 - 12, 12, 64, 64}; + + windowData.dropZone = gfx::Rect(spec.frame().w-64-12, 12, 64, 64); redraw_window(newWindow.get()); return newWindow; } @@ -182,15 +190,8 @@ int app_main(int argc, char* argv[]) system->setAppMode(os::AppMode::GUI); system->handleWindowResize = redraw_window; - auto screen = system->mainScreen(); - os::WindowSpec spec; DragTarget dragTarget; - spec.titled(true); - spec.position(os::WindowSpec::Position::Frame); - auto frame = screen->workarea()/2; - spec.frame(frame); - spec.screen(screen); - os::WindowRef window = create_window("Drag & Drop example", spec, dragTarget); + os::WindowRef window = create_window(dragTarget); bool running = true; From 84736809d18a02fe4b06bf73bd528e9bc71e4466 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 7 May 2024 11:52:59 -0300 Subject: [PATCH 27/41] Minor formatting/clean up changes in drag_and_drop example --- examples/drag_and_drop.cpp | 97 +++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index cdfa16c76..f368db063 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -4,26 +4,11 @@ // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. - #include "base/paths.h" -#include "gfx/hsv.h" -#include "gfx/point.h" -#include "gfx/rect.h" -#include "gfx/rgb.h" -#include "os/dnd.h" -#include "os/draw_text.h" #include "os/os.h" -#include "os/paint.h" -#include "os/surface.h" -#include -#include #include -#include -#include -#include - -using Boxes = std::vector; +#include struct WindowData { bool dragEnter; @@ -42,48 +27,54 @@ static void redraw_window(os::Window* window); class DragTarget : public os::DragTarget { public: - void dragEnter(os::DragEvent& ev) override { - if (!windowData.dropZone.contains(ev.position()) || !ev.sourceSupports(os::DropOperation::Copy)) - ev.dropResult(os::DropOperation::None); - else if (ev.sourceSupports(os::DropOperation::Copy)) - ev.dropResult(os::DropOperation::Copy); - - windowData.dragEnter = true; - windowData.dragLeave = false; - windowData.drag = 0; - windowData.dragPosition = ev.position(); - redraw_window(ev.target()); + void dragEnter(os::DragEvent& ev) override { + if (!windowData.dropZone.contains(ev.position()) || + !ev.sourceSupports(os::DropOperation::Copy)) { + ev.dropResult(os::DropOperation::None); } - void dragLeave(os::DragEvent& ev) override { - windowData.dragEnter = false; - windowData.dragLeave = true; - windowData.dragPosition = ev.position(); - redraw_window(ev.target()); + else if (ev.sourceSupports(os::DropOperation::Copy)) { + ev.dropResult(os::DropOperation::Copy); } - void drag(os::DragEvent& ev) override { - ++windowData.drag; - windowData.dragPosition = ev.position(); - redraw_window(ev.target()); - } - void drop(os::DragEvent& ev) override { - windowData.dragEnter = false; - windowData.dragLeave = false; - windowData.dragPosition = {0, 0}; - ev.acceptDrop(windowData.dropZone.contains(ev.position())); - - if (ev.acceptDrop()) { - if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) - windowData.paths = ev.dataProvider()->getPaths(); + + windowData.dragEnter = true; + windowData.dragLeave = false; + windowData.drag = 0; + windowData.dragPosition = ev.position(); + redraw_window(ev.target()); + } + + void dragLeave(os::DragEvent& ev) override { + windowData.dragEnter = false; + windowData.dragLeave = true; + windowData.dragPosition = ev.position(); + redraw_window(ev.target()); + } + + void drag(os::DragEvent& ev) override { + ++windowData.drag; + windowData.dragPosition = ev.position(); + redraw_window(ev.target()); + } + + void drop(os::DragEvent& ev) override { + windowData.dragEnter = false; + windowData.dragLeave = false; + windowData.dragPosition = { 0, 0 }; + ev.acceptDrop(windowData.dropZone.contains(ev.position())); + + if (ev.acceptDrop()) { + if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) + windowData.paths = ev.dataProvider()->getPaths(); #if CLIP_ENABLE_IMAGE - if (ev.dataProvider()->contains(os::DragDataItemType::Image)) - windowData.image = ev.dataProvider()->getImage(); + if (ev.dataProvider()->contains(os::DragDataItemType::Image)) + windowData.image = ev.dataProvider()->getImage(); #endif - if (ev.dataProvider()->contains(os::DragDataItemType::Url)) - windowData.url = ev.dataProvider()->getUrl(); - } - - redraw_window(ev.target()); + if (ev.dataProvider()->contains(os::DragDataItemType::Url)) + windowData.url = ev.dataProvider()->getUrl(); } + + redraw_window(ev.target()); + } }; static void redraw_window(os::Window* window) From fefb4371ad14bbd4692bb270717421d201942fb7 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 7 May 2024 12:02:35 -0300 Subject: [PATCH 28/41] Move os::CommonSystem impl to its own .cpp file --- os/CMakeLists.txt | 3 +- os/common/system.cpp | 186 +++++++++++++++++++++++++++++++++++++++++++ os/common/system.h | 166 +++----------------------------------- 3 files changed, 197 insertions(+), 158 deletions(-) create mode 100644 os/common/system.cpp diff --git a/os/CMakeLists.txt b/os/CMakeLists.txt index 67d135a9c..4e8cf9eaa 100644 --- a/os/CMakeLists.txt +++ b/os/CMakeLists.txt @@ -1,5 +1,5 @@ # LAF OS -# Copyright (C) 2018-2023 Igara Studio S.A. +# Copyright (C) 2018-2024 Igara Studio S.A. # Copyright (C) 2012-2018 David Capello ###################################################################### @@ -8,6 +8,7 @@ set(LAF_OS_SOURCES common/event_queue.cpp common/main.cpp + common/system.cpp dnd.cpp system.cpp window.cpp) diff --git a/os/common/system.cpp b/os/common/system.cpp new file mode 100644 index 000000000..dfe520a70 --- /dev/null +++ b/os/common/system.cpp @@ -0,0 +1,186 @@ +// LAF OS Library +// Copyright (C) 2019-2024 Igara Studio S.A. +// Copyright (C) 2012-2018 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "os/common/system.h" + +#if LAF_WINDOWS + #include "os/win/native_dialogs.h" +#elif LAF_MACOS + #include "os/osx/native_dialogs.h" +#elif LAF_LINUX + #include "os/x11/native_dialogs.h" +#else + #include "os/native_dialogs.h" +#endif + +namespace os { + +CommonSystem::CommonSystem() +{ +} + +CommonSystem::~CommonSystem() +{ + // destroyInstance() can be called multiple times by derived + // classes. + if (instance() == this) + destroyInstance(); +} + +NativeDialogs* CommonSystem::nativeDialogs() +{ + if (!m_nativeDialogs) { +#if LAF_WINDOWS + m_nativeDialogs = Ref(new NativeDialogsWin); +#elif LAF_MACOS + m_nativeDialogs = Ref(new NativeDialogsOSX); +#elif LAF_LINUX + m_nativeDialogs = Ref(new NativeDialogsX11); +#endif + } + return m_nativeDialogs.get(); +} + +FontRef CommonSystem::loadSpriteSheetFont(const char* filename, int scale) +{ + SurfaceRef sheet = loadRgbaSurface(filename); + FontRef font = nullptr; + if (sheet) { + sheet->applyScale(scale); + sheet->setImmutable(); + font = SpriteSheetFont::fromSurface(sheet); + } + return font; +} + +FontRef CommonSystem::loadTrueTypeFont(const char* filename, int height) +{ +#ifdef LAF_FREETYPE + if (!m_ft) + m_ft.reset(new ft::Lib()); + return FontRef(load_free_type_font(*m_ft.get(), filename, height)); +#else + return nullptr; +#endif +} + +KeyModifiers CommonSystem::keyModifiers() +{ + return + (KeyModifiers) + ((isKeyPressed(kKeyLShift) || + isKeyPressed(kKeyRShift) ? kKeyShiftModifier: 0) | + (isKeyPressed(kKeyLControl) || + isKeyPressed(kKeyRControl) ? kKeyCtrlModifier: 0) | + (isKeyPressed(kKeyAlt) ? kKeyAltModifier: 0) | + (isKeyPressed(kKeyAltGr) ? (kKeyCtrlModifier | kKeyAltModifier): 0) | + (isKeyPressed(kKeyCommand) ? kKeyCmdModifier: 0) | + (isKeyPressed(kKeySpace) ? kKeySpaceModifier: 0) | + (isKeyPressed(kKeyLWin) || + isKeyPressed(kKeyRWin) ? kKeyWinModifier: 0)); +} + +#if CLIP_ENABLE_IMAGE + +SurfaceRef CommonSystem::makeSurface(const clip::image& image) +{ + const clip::image_spec spec = image.spec(); + + if (spec.bits_per_pixel != 32 && + spec.bits_per_pixel != 24 && + spec.bits_per_pixel != 16) + return nullptr; + + SurfaceRef surface = ((System*)this)->makeRgbaSurface(spec.width, spec.height); + SurfaceFormatData sfd; + surface->getFormat(&sfd); + + auto get_rgba32 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { + uint32_t c = *((uint32_t*)scanlineAddr); + if (spec.alpha_mask) + *a = ((c & spec.alpha_mask) >> spec.alpha_shift); + else + *a = 255; + // The source image is in straight-alpha and makeRgbaSurface returns a + // surface using premultiplied-alpha so we have to premultiply the + // source values. + *r = ((c & spec.red_mask) >> spec.red_shift )*(*a)/255; + *g = ((c & spec.green_mask)>> spec.green_shift)*(*a)/255; + *b = ((c & spec.blue_mask) >> spec.blue_shift )*(*a)/255; + }; + auto get_rgba24 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { + uint32_t c = *((uint32_t*)scanlineAddr); + *r = ((c & spec.red_mask) >> spec.red_shift); + *g = ((c & spec.green_mask)>> spec.green_shift); + *b = ((c & spec.blue_mask) >> spec.blue_shift); + *a = 255; + }; + auto get_rgba16 = [&spec](char* scanlineAddr, int* r, int* g, int *b, int *a) { + uint16_t c = *((uint16_t*)scanlineAddr); + *r = (((c & spec.red_mask )>>spec.red_shift )*255) / (spec.red_mask >>spec.red_shift); + *g = (((c & spec.green_mask)>>spec.green_shift)*255) / (spec.green_mask>>spec.green_shift); + *b = (((c & spec.blue_mask )>>spec.blue_shift )*255) / (spec.blue_mask >>spec.blue_shift); + *a = 255; + }; + + // Select color components retrieval function. + std::function get_rgba; + switch (spec.bits_per_pixel) { + case 32: + get_rgba = get_rgba32; + break; + case 24: + get_rgba = get_rgba24; + break; + case 16: + get_rgba = get_rgba16; + break; + } + + for (int v=0; vgetData(0, v); + char* src = image.data() + v * spec.bytes_per_row; + for (int u=0; uclearEvents(); + + set_instance(nullptr); +} + +} // namespace os diff --git a/os/common/system.h b/os/common/system.h index 195d69830..d227ba388 100644 --- a/os/common/system.h +++ b/os/common/system.h @@ -1,5 +1,5 @@ // LAF OS Library -// Copyright (C) 2019-2023 Igara Studio S.A. +// Copyright (C) 2019-2024 Igara Studio S.A. // Copyright (C) 2012-2018 David Capello // // This file is released under the terms of the MIT license. @@ -9,18 +9,6 @@ #define OS_COMMON_SYSTEM_H #pragma once -#if LAF_WINDOWS - #include "os/win/native_dialogs.h" -#elif LAF_MACOS - #include "os/osx/app.h" - #include "os/osx/menus.h" - #include "os/osx/native_dialogs.h" -#elif LAF_LINUX - #include "os/x11/native_dialogs.h" -#else - #include "os/native_dialogs.h" -#endif - #ifdef LAF_FREETYPE #include "ft/lib.h" #include "os/common/freetype_font.h" @@ -34,19 +22,12 @@ #include "clip/clip.h" #endif -#include - namespace os { class CommonSystem : public System { public: - CommonSystem() { } - ~CommonSystem() { - // destroyInstance() can be called multiple times by derived - // classes. - if (instance() == this) - destroyInstance(); - } + CommonSystem(); + ~CommonSystem(); void setAppName(const std::string& appName) override { } void setAppMode(AppMode appMode) override { } @@ -71,133 +52,19 @@ class CommonSystem : public System { return nullptr; } - NativeDialogs* nativeDialogs() override { - if (!m_nativeDialogs) { -#if LAF_WINDOWS - m_nativeDialogs = Ref(new NativeDialogsWin); -#elif LAF_MACOS - m_nativeDialogs = Ref(new NativeDialogsOSX); -#elif LAF_LINUX - m_nativeDialogs = Ref(new NativeDialogsX11); -#endif - } - return m_nativeDialogs.get(); - } + NativeDialogs* nativeDialogs() override; EventQueue* eventQueue() override { return EventQueue::instance(); } - FontRef loadSpriteSheetFont(const char* filename, int scale) override { - SurfaceRef sheet = loadRgbaSurface(filename); - FontRef font = nullptr; - if (sheet) { - sheet->applyScale(scale); - sheet->setImmutable(); - font = SpriteSheetFont::fromSurface(sheet); - } - return font; - } - - FontRef loadTrueTypeFont(const char* filename, int height) override { -#ifdef LAF_FREETYPE - if (!m_ft) - m_ft.reset(new ft::Lib()); - return FontRef(load_free_type_font(*m_ft.get(), filename, height)); -#else - return nullptr; -#endif - } + FontRef loadSpriteSheetFont(const char* filename, int scale) override; + FontRef loadTrueTypeFont(const char* filename, int height) override; - KeyModifiers keyModifiers() override { - return - (KeyModifiers) - ((isKeyPressed(kKeyLShift) || - isKeyPressed(kKeyRShift) ? kKeyShiftModifier: 0) | - (isKeyPressed(kKeyLControl) || - isKeyPressed(kKeyRControl) ? kKeyCtrlModifier: 0) | - (isKeyPressed(kKeyAlt) ? kKeyAltModifier: 0) | - (isKeyPressed(kKeyAltGr) ? (kKeyCtrlModifier | kKeyAltModifier): 0) | - (isKeyPressed(kKeyCommand) ? kKeyCmdModifier: 0) | - (isKeyPressed(kKeySpace) ? kKeySpaceModifier: 0) | - (isKeyPressed(kKeyLWin) || - isKeyPressed(kKeyRWin) ? kKeyWinModifier: 0)); - } + KeyModifiers keyModifiers() override; #if CLIP_ENABLE_IMAGE - - SurfaceRef makeSurface(const clip::image& image) override - { - const clip::image_spec spec = image.spec(); - - if (spec.bits_per_pixel != 32 && - spec.bits_per_pixel != 24 && - spec.bits_per_pixel != 16) - return nullptr; - - SurfaceRef surface = ((System*)this)->makeRgbaSurface(spec.width, spec.height); - SurfaceFormatData sfd; - surface->getFormat(&sfd); - - auto get_rgba32 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { - uint32_t c = *((uint32_t*)scanlineAddr); - if (spec.alpha_mask) - *a = ((c & spec.alpha_mask) >> spec.alpha_shift); - else - *a = 255; - // The source image is in straight-alpha and makeRgbaSurface returns a - // surface using premultiplied-alpha so we have to premultiply the - // source values. - *r = ((c & spec.red_mask) >> spec.red_shift )*(*a)/255; - *g = ((c & spec.green_mask)>> spec.green_shift)*(*a)/255; - *b = ((c & spec.blue_mask) >> spec.blue_shift )*(*a)/255; - }; - auto get_rgba24 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { - uint32_t c = *((uint32_t*)scanlineAddr); - *r = ((c & spec.red_mask) >> spec.red_shift); - *g = ((c & spec.green_mask)>> spec.green_shift); - *b = ((c & spec.blue_mask) >> spec.blue_shift); - *a = 255; - }; - auto get_rgba16 = [&spec](char* scanlineAddr, int* r, int* g, int *b, int *a) { - uint16_t c = *((uint16_t*)scanlineAddr); - *r = (((c & spec.red_mask )>>spec.red_shift )*255) / (spec.red_mask >>spec.red_shift); - *g = (((c & spec.green_mask)>>spec.green_shift)*255) / (spec.green_mask>>spec.green_shift); - *b = (((c & spec.blue_mask )>>spec.blue_shift )*255) / (spec.blue_mask >>spec.blue_shift); - *a = 255; - }; - - // Select color components retrieval function. - std::function get_rgba; - switch (spec.bits_per_pixel) { - case 32: - get_rgba = get_rgba32; - break; - case 24: - get_rgba = get_rgba24; - break; - case 16: - get_rgba = get_rgba16; - break; - } - - for (int v=0; vgetData(0, v); - char* src = image.data() + v * spec.bytes_per_row; - for (int u=0; uclearEvents(); - - set_instance(nullptr); - } + void destroyInstance(); private: Ref m_nativeDialogs; From 672c903cc79317841a98c08eabffe5a00840e5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 7 May 2024 16:21:12 -0300 Subject: [PATCH 29/41] [osx] Fix includes to get MenusOSX declaration --- os/osx/system.h | 1 + 1 file changed, 1 insertion(+) diff --git a/os/osx/system.h b/os/osx/system.h index 6b3599062..67f1ef11b 100644 --- a/os/osx/system.h +++ b/os/osx/system.h @@ -14,6 +14,7 @@ #include "os/menus.h" #include "os/osx/app.h" #include "os/osx/logger.h" +#include "os/osx/menus.h" namespace os { From 643ec4912572c2f4c1fe76aef9832a3714ebb41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 7 May 2024 16:23:19 -0300 Subject: [PATCH 30/41] Replace use of std::function and lambdas by function pointer and free functions --- os/common/system.cpp | 65 ++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/os/common/system.cpp b/os/common/system.cpp index dfe520a70..61bb28093 100644 --- a/os/common/system.cpp +++ b/os/common/system.cpp @@ -90,6 +90,39 @@ KeyModifiers CommonSystem::keyModifiers() #if CLIP_ENABLE_IMAGE +void get_rgba32(const clip::image_spec& spec, const char* scanlineAddr, int* r, int* g, int* b, int* a) +{ + uint32_t c = *((uint32_t*)scanlineAddr); + if (spec.alpha_mask) + *a = ((c & spec.alpha_mask) >> spec.alpha_shift); + else + *a = 255; + // The source image is in straight-alpha and makeRgbaSurface returns a + // surface using premultiplied-alpha so we have to premultiply the + // source values. + *r = ((c & spec.red_mask) >> spec.red_shift )*(*a)/255; + *g = ((c & spec.green_mask)>> spec.green_shift)*(*a)/255; + *b = ((c & spec.blue_mask) >> spec.blue_shift )*(*a)/255; +} + +void get_rgba24(const clip::image_spec& spec, const char* scanlineAddr, int* r, int* g, int* b, int* a) +{ + uint32_t c = *((uint32_t*)scanlineAddr); + *r = ((c & spec.red_mask) >> spec.red_shift); + *g = ((c & spec.green_mask)>> spec.green_shift); + *b = ((c & spec.blue_mask) >> spec.blue_shift); + *a = 255; +} + +void get_rgba16(const clip::image_spec& spec, const char* scanlineAddr, int* r, int* g, int* b, int* a) +{ + uint16_t c = *((uint16_t*)scanlineAddr); + *r = (((c & spec.red_mask )>>spec.red_shift )*255) / (spec.red_mask >>spec.red_shift); + *g = (((c & spec.green_mask)>>spec.green_shift)*255) / (spec.green_mask>>spec.green_shift); + *b = (((c & spec.blue_mask )>>spec.blue_shift )*255) / (spec.blue_mask >>spec.blue_shift); + *a = 255; +} + SurfaceRef CommonSystem::makeSurface(const clip::image& image) { const clip::image_spec spec = image.spec(); @@ -103,36 +136,8 @@ SurfaceRef CommonSystem::makeSurface(const clip::image& image) SurfaceFormatData sfd; surface->getFormat(&sfd); - auto get_rgba32 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { - uint32_t c = *((uint32_t*)scanlineAddr); - if (spec.alpha_mask) - *a = ((c & spec.alpha_mask) >> spec.alpha_shift); - else - *a = 255; - // The source image is in straight-alpha and makeRgbaSurface returns a - // surface using premultiplied-alpha so we have to premultiply the - // source values. - *r = ((c & spec.red_mask) >> spec.red_shift )*(*a)/255; - *g = ((c & spec.green_mask)>> spec.green_shift)*(*a)/255; - *b = ((c & spec.blue_mask) >> spec.blue_shift )*(*a)/255; - }; - auto get_rgba24 = [&spec](char* scanlineAddr, int* r, int* g, int* b, int* a) { - uint32_t c = *((uint32_t*)scanlineAddr); - *r = ((c & spec.red_mask) >> spec.red_shift); - *g = ((c & spec.green_mask)>> spec.green_shift); - *b = ((c & spec.blue_mask) >> spec.blue_shift); - *a = 255; - }; - auto get_rgba16 = [&spec](char* scanlineAddr, int* r, int* g, int *b, int *a) { - uint16_t c = *((uint16_t*)scanlineAddr); - *r = (((c & spec.red_mask )>>spec.red_shift )*255) / (spec.red_mask >>spec.red_shift); - *g = (((c & spec.green_mask)>>spec.green_shift)*255) / (spec.green_mask>>spec.green_shift); - *b = (((c & spec.blue_mask )>>spec.blue_shift )*255) / (spec.blue_mask >>spec.blue_shift); - *a = 255; - }; - // Select color components retrieval function. - std::function get_rgba; + void (*get_rgba)(const clip::image_spec&, const char*, int*, int*, int*, int*); switch (spec.bits_per_pixel) { case 32: get_rgba = get_rgba32; @@ -150,7 +155,7 @@ SurfaceRef CommonSystem::makeSurface(const clip::image& image) char* src = image.data() + v * spec.bytes_per_row; for (int u=0; u Date: Tue, 7 May 2024 16:31:53 -0300 Subject: [PATCH 31/41] Use uint8_t* instead of char* for the address of the source's image pixels --- os/common/system.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/os/common/system.cpp b/os/common/system.cpp index 61bb28093..76dab7ab6 100644 --- a/os/common/system.cpp +++ b/os/common/system.cpp @@ -90,7 +90,7 @@ KeyModifiers CommonSystem::keyModifiers() #if CLIP_ENABLE_IMAGE -void get_rgba32(const clip::image_spec& spec, const char* scanlineAddr, int* r, int* g, int* b, int* a) +void get_rgba32(const clip::image_spec& spec, const uint8_t* scanlineAddr, int* r, int* g, int* b, int* a) { uint32_t c = *((uint32_t*)scanlineAddr); if (spec.alpha_mask) @@ -105,7 +105,7 @@ void get_rgba32(const clip::image_spec& spec, const char* scanlineAddr, int* r, *b = ((c & spec.blue_mask) >> spec.blue_shift )*(*a)/255; } -void get_rgba24(const clip::image_spec& spec, const char* scanlineAddr, int* r, int* g, int* b, int* a) +void get_rgba24(const clip::image_spec& spec, const uint8_t* scanlineAddr, int* r, int* g, int* b, int* a) { uint32_t c = *((uint32_t*)scanlineAddr); *r = ((c & spec.red_mask) >> spec.red_shift); @@ -114,7 +114,7 @@ void get_rgba24(const clip::image_spec& spec, const char* scanlineAddr, int* r, *a = 255; } -void get_rgba16(const clip::image_spec& spec, const char* scanlineAddr, int* r, int* g, int* b, int* a) +void get_rgba16(const clip::image_spec& spec, const uint8_t* scanlineAddr, int* r, int* g, int* b, int* a) { uint16_t c = *((uint16_t*)scanlineAddr); *r = (((c & spec.red_mask )>>spec.red_shift )*255) / (spec.red_mask >>spec.red_shift); @@ -137,7 +137,7 @@ SurfaceRef CommonSystem::makeSurface(const clip::image& image) surface->getFormat(&sfd); // Select color components retrieval function. - void (*get_rgba)(const clip::image_spec&, const char*, int*, int*, int*, int*); + void (*get_rgba)(const clip::image_spec&, const uint8_t*, int*, int*, int*, int*); switch (spec.bits_per_pixel) { case 32: get_rgba = get_rgba32; @@ -152,7 +152,7 @@ SurfaceRef CommonSystem::makeSurface(const clip::image& image) for (int v=0; vgetData(0, v); - char* src = image.data() + v * spec.bytes_per_row; + const uint8_t* src = ((uint8_t*)image.data()) + v * spec.bytes_per_row; for (int u=0; u Date: Tue, 7 May 2024 16:56:37 -0300 Subject: [PATCH 32/41] Move decoding related functions into os namespace --- os/dnd.cpp | 10 +++++----- os/dnd.h | 21 ++++++++++----------- os/skia/skia_system.h | 1 - os/win/dnd.cpp | 12 ++++++------ 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/os/dnd.cpp b/os/dnd.cpp index 384aea4ad..1b8155f43 100644 --- a/os/dnd.cpp +++ b/os/dnd.cpp @@ -13,13 +13,13 @@ #include "os/dnd.h" #include "os/system.h" -static os::DecoderFunc g_decode_png = default_decode_png; -static os::DecoderFunc g_decode_jpg = default_decode_jpg; -static os::DecoderFunc g_decode_bmp = default_decode_bmp; -static os::DecoderFunc g_decode_gif = default_decode_gif; - namespace os { +static DecoderFunc g_decode_png = default_decode_png; +static DecoderFunc g_decode_jpg = default_decode_jpg; +static DecoderFunc g_decode_bmp = default_decode_bmp; +static DecoderFunc g_decode_gif = default_decode_gif; + void set_decode_png(DecoderFunc func) { g_decode_png = func; diff --git a/os/dnd.h b/os/dnd.h index 0c80a8713..72979beff 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -13,23 +13,22 @@ #include "gfx/point.h" #include "os/surface.h" -#include #include #pragma push_macro("None") #undef None // Undefine the X11 None macro -os::SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len); -os::SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len); -os::SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len); -os::SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len); - namespace os { + SurfaceRef default_decode_png(const uint8_t* buf, uint32_t len); + SurfaceRef default_decode_jpg(const uint8_t* buf, uint32_t len); + SurfaceRef default_decode_bmp(const uint8_t* buf, uint32_t len); + SurfaceRef default_decode_gif(const uint8_t* buf, uint32_t len); + class Window; #if CLIP_ENABLE_IMAGE - using DecoderFunc = std::function; + using DecoderFunc = SurfaceRef(*)(const uint8_t* buf, uint32_t len); // Methods used to configure custom decoder functions by replacing the default implementations. @@ -38,10 +37,10 @@ namespace os { void set_decode_bmp(DecoderFunc func); void set_decode_gif(DecoderFunc func); - SurfaceRef decode_png(const uint8_t* buf, const uint32_t len); - SurfaceRef decode_jpg(const uint8_t* buf, const uint32_t len); - SurfaceRef decode_bmp(const uint8_t* buf, const uint32_t len); - SurfaceRef decode_gif(const uint8_t* buf, const uint32_t len); + SurfaceRef decode_png(const uint8_t* buf, uint32_t len); + SurfaceRef decode_jpg(const uint8_t* buf, uint32_t len); + SurfaceRef decode_bmp(const uint8_t* buf, uint32_t len); + SurfaceRef decode_gif(const uint8_t* buf, uint32_t len); #endif // Operations that can be supported by source and target windows in a drag // and drop operation. diff --git a/os/skia/skia_system.h b/os/skia/skia_system.h index 557692d16..2d9d85119 100644 --- a/os/skia/skia_system.h +++ b/os/skia/skia_system.h @@ -104,7 +104,6 @@ class SkiaSystem final : public SkiaSystemBase { return sur; } - SurfaceRef makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace) override { auto sur = make_ref(); diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index c00dc18f2..3f2dca47b 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -132,8 +132,10 @@ class DataWrapper { } // anonymous namespace +namespace os { + #if CLIP_ENABLE_IMAGE -os::SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len) +SurfaceRef default_decode_png(const uint8_t* buf, uint32_t len) { clip::image img; if (!clip::win::read_png(buf, len, &img, nullptr)) @@ -142,7 +144,7 @@ os::SurfaceRef default_decode_png(const uint8_t* buf, const uint32_t len) return os::instance()->makeSurface(img); } -os::SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len) +SurfaceRef default_decode_jpg(const uint8_t* buf, uint32_t len) { clip::image img; if (!clip::win::read_jpg(buf, len, &img, nullptr)) @@ -151,7 +153,7 @@ os::SurfaceRef default_decode_jpg(const uint8_t* buf, const uint32_t len) return os::instance()->makeSurface(img); } -os::SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len) +SurfaceRef default_decode_bmp(const uint8_t* buf, uint32_t len) { clip::image img; if (!clip::win::read_bmp(buf, len, &img, nullptr)) @@ -160,7 +162,7 @@ os::SurfaceRef default_decode_bmp(const uint8_t* buf, const uint32_t len) return os::instance()->makeSurface(img); } -os::SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len) +SurfaceRef default_decode_gif(const uint8_t* buf, uint32_t len) { clip::image img; if (!clip::win::read_gif(buf, len, &img, nullptr)) @@ -170,8 +172,6 @@ os::SurfaceRef default_decode_gif(const uint8_t* buf, const uint32_t len) } #endif -namespace os { - base::paths DragDataProviderWin::getPaths() { base::paths files; From 269aefade5a84a2ebc1b1d405c978b5543efad41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 7 May 2024 17:32:37 -0300 Subject: [PATCH 33/41] [osx] Move free functions inside os namespace --- os/osx/dnd.h | 9 +++++---- os/osx/dnd.mm | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/os/osx/dnd.h b/os/osx/dnd.h index 0f2dd85bb..749e332b2 100644 --- a/os/osx/dnd.h +++ b/os/osx/dnd.h @@ -33,11 +33,12 @@ namespace os { bool contains(DragDataItemType type) override; }; -} // namespace os -NSDragOperation as_nsdragoperation(const os::DropOperation op); -os::DropOperation as_dropoperation(const NSDragOperation nsdop); -gfx::Point drag_position(id sender); + NSDragOperation as_nsdragoperation(const os::DropOperation op); + os::DropOperation as_dropoperation(const NSDragOperation nsdop); + gfx::Point drag_position(id sender); + +} // namespace os #endif diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm index 067bc707c..ecdfca80f 100644 --- a/os/osx/dnd.mm +++ b/os/osx/dnd.mm @@ -69,8 +69,6 @@ return false; } -} // namespace os - NSDragOperation as_nsdragoperation(const os::DropOperation op) { NSDragOperation nsdop; @@ -109,4 +107,6 @@ NSDragOperation as_nsdragoperation(const os::DropOperation op) contentRect.size.height - sender.draggingLocation.y); } +} // namespace os + #endif From e0d4317a0bc0c9f50779c1299ead701c803c26be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Tue, 7 May 2024 18:10:37 -0300 Subject: [PATCH 34/41] [osx] Take into account the window scale when calculating the drag position --- os/osx/dnd.mm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm index ecdfca80f..f65afae33 100644 --- a/os/osx/dnd.mm +++ b/os/osx/dnd.mm @@ -10,6 +10,7 @@ #include "clip/clip_osx.h" #include "os/dnd.h" #include "os/osx/dnd.h" +#include "os/osx/window.h" #include "os/surface_format.h" #include "os/system.h" @@ -101,10 +102,10 @@ NSDragOperation as_nsdragoperation(const os::DropOperation op) gfx::Point drag_position(id sender) { - NSRect contentRect = [sender.draggingDestinationWindow contentRectForFrameRect:sender.draggingDestinationWindow.frame]; - return gfx::Point( - sender.draggingLocation.x, - contentRect.size.height - sender.draggingLocation.y); + Window* target = [(WindowOSXObjc*)sender.draggingDestinationWindow impl]; + return target->pointFromScreen( + gfx::Point(target->contentRect().x + sender.draggingLocation.x, + target->contentRect().y + target->contentRect().h - sender.draggingLocation.y)); } } // namespace os From 7fcfeb011883345df2902fdc2717ba57f0e1e800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Wed, 8 May 2024 10:12:05 -0300 Subject: [PATCH 35/41] Convert some SkiaSurface private static member functions into internal free functions --- os/skia/skia_surface.cpp | 165 +++++++++++++++++++-------------------- os/skia/skia_surface.h | 3 - 2 files changed, 81 insertions(+), 87 deletions(-) diff --git a/os/skia/skia_surface.cpp b/os/skia/skia_surface.cpp index c963edea0..e5e3d09ff 100644 --- a/os/skia/skia_surface.cpp +++ b/os/skia/skia_surface.cpp @@ -38,6 +38,86 @@ namespace os { +SkColorType deductSkColorType(const os::SurfaceFormatData& sf) +{ + if (sf.redShift == SK_RGBA_R32_SHIFT && + sf.greenShift == SK_RGBA_G32_SHIFT && + sf.blueShift == SK_RGBA_B32_SHIFT && + sf.alphaShift == SK_RGBA_A32_SHIFT && + sf.redMask == (255 << SK_RGBA_R32_SHIFT) && + sf.greenMask == (255 << SK_RGBA_G32_SHIFT) && + sf.blueMask == (255 << SK_RGBA_B32_SHIFT) && + sf.alphaMask == (255 << SK_RGBA_A32_SHIFT)) + return kRGBA_8888_SkColorType; + + if (sf.redShift == SK_RGBA_R32_SHIFT && + sf.greenShift == SK_RGBA_G32_SHIFT && + sf.blueShift == SK_RGBA_B32_SHIFT && + sf.redMask == (255 << SK_RGBA_R32_SHIFT) && + sf.greenMask == (255 << SK_RGBA_G32_SHIFT) && + sf.blueMask == (255 << SK_RGBA_B32_SHIFT) && + sf.alphaMask == 0) + return kRGB_888x_SkColorType; + + if (sf.redShift == SK_BGRA_R32_SHIFT && + sf.greenShift == SK_BGRA_G32_SHIFT && + sf.blueShift == SK_BGRA_B32_SHIFT && + sf.alphaShift == SK_BGRA_A32_SHIFT && + sf.redMask == (255 << SK_BGRA_R32_SHIFT) && + sf.greenMask == (255 << SK_BGRA_G32_SHIFT) && + sf.blueMask == (255 << SK_BGRA_B32_SHIFT) && + sf.alphaMask == (255 << SK_BGRA_A32_SHIFT)) + return kBGRA_8888_SkColorType; + + if (sf.redShift == SK_R4444_SHIFT && + sf.greenShift == SK_G4444_SHIFT && + sf.blueShift == SK_B4444_SHIFT && + sf.alphaShift == SK_A4444_SHIFT && + sf.redMask == (15 << SK_R4444_SHIFT) && + sf.greenMask == (15 << SK_G4444_SHIFT) && + sf.blueMask == (15 << SK_B4444_SHIFT) && + sf.alphaMask == (15 << SK_A4444_SHIFT)) + return kARGB_4444_SkColorType; + + if (sf.redShift == SK_R16_SHIFT && + sf.greenShift == SK_G16_SHIFT && + sf.blueShift == SK_B16_SHIFT && + sf.alphaShift == 0 && + sf.redMask == SK_R16_MASK && + sf.greenMask == SK_G16_MASK && + sf.blueMask == SK_B16_MASK && + sf.alphaMask == 0) + return kRGB_565_SkColorType; + + return kUnknown_SkColorType; +} + +os::PixelAlpha asPixelAlpha(SkAlphaType at) +{ + switch(at) { + case SkAlphaType::kOpaque_SkAlphaType: + return os::PixelAlpha::kOpaque; + case SkAlphaType::kPremul_SkAlphaType: + return os::PixelAlpha::kPremultiplied; + case SkAlphaType::kUnpremul_SkAlphaType: + return os::PixelAlpha::kStraight; + default: + throw base::Exception("Unsupported alpha type"); + } +} + +SkAlphaType asSkAlphaType(os::PixelAlpha pa) +{ + switch(pa) { + case os::PixelAlpha::kOpaque: + return SkAlphaType::kOpaque_SkAlphaType; + case os::PixelAlpha::kPremultiplied: + return SkAlphaType::kPremul_SkAlphaType; + case os::PixelAlpha::kStraight: + return SkAlphaType::kUnpremul_SkAlphaType; + } +} + SkiaSurface::SkiaSurface() : m_surface(nullptr) , m_colorSpace(nullptr) @@ -277,7 +357,7 @@ void SkiaSurface::getFormat(SurfaceFormatData* formatData) const { formatData->format = kRgbaSurfaceFormat; formatData->bitsPerPixel = 8*m_bitmap.bytesPerPixel(); - formatData->pixelAlpha = SkiaSurface::asPixelAlpha(m_bitmap.alphaType()); + formatData->pixelAlpha = asPixelAlpha(m_bitmap.alphaType()); switch (m_bitmap.colorType()) { case kRGB_565_SkColorType: @@ -333,89 +413,6 @@ void SkiaSurface::getFormat(SurfaceFormatData* formatData) const } } -// static -SkColorType SkiaSurface::deductSkColorType(const os::SurfaceFormatData& sf) -{ - if (sf.redShift == SK_RGBA_R32_SHIFT && - sf.greenShift == SK_RGBA_G32_SHIFT && - sf.blueShift == SK_RGBA_B32_SHIFT && - sf.alphaShift == SK_RGBA_A32_SHIFT && - sf.redMask == (255 << SK_RGBA_R32_SHIFT) && - sf.greenMask == (255 << SK_RGBA_G32_SHIFT) && - sf.blueMask == (255 << SK_RGBA_B32_SHIFT) && - sf.alphaMask == (255 << SK_RGBA_A32_SHIFT)) - return kRGBA_8888_SkColorType; - - if (sf.redShift == SK_RGBA_R32_SHIFT && - sf.greenShift == SK_RGBA_G32_SHIFT && - sf.blueShift == SK_RGBA_B32_SHIFT && - sf.redMask == (255 << SK_RGBA_R32_SHIFT) && - sf.greenMask == (255 << SK_RGBA_G32_SHIFT) && - sf.blueMask == (255 << SK_RGBA_B32_SHIFT) && - sf.alphaMask == 0) - return kRGB_888x_SkColorType; - - if (sf.redShift == SK_BGRA_R32_SHIFT && - sf.greenShift == SK_BGRA_G32_SHIFT && - sf.blueShift == SK_BGRA_B32_SHIFT && - sf.alphaShift == SK_BGRA_A32_SHIFT && - sf.redMask == (255 << SK_BGRA_R32_SHIFT) && - sf.greenMask == (255 << SK_BGRA_G32_SHIFT) && - sf.blueMask == (255 << SK_BGRA_B32_SHIFT) && - sf.alphaMask == (255 << SK_BGRA_A32_SHIFT)) - return kBGRA_8888_SkColorType; - - if (sf.redShift == SK_R4444_SHIFT && - sf.greenShift == SK_G4444_SHIFT && - sf.blueShift == SK_B4444_SHIFT && - sf.alphaShift == SK_A4444_SHIFT && - sf.redMask == (15 << SK_R4444_SHIFT) && - sf.greenMask == (15 << SK_G4444_SHIFT) && - sf.blueMask == (15 << SK_B4444_SHIFT) && - sf.alphaMask == (15 << SK_A4444_SHIFT)) - return kARGB_4444_SkColorType; - - if (sf.redShift == SK_R16_SHIFT && - sf.greenShift == SK_G16_SHIFT && - sf.blueShift == SK_B16_SHIFT && - sf.alphaShift == 0 && - sf.redMask == SK_R16_MASK && - sf.greenMask == SK_G16_MASK && - sf.blueMask == SK_B16_MASK && - sf.alphaMask == 0) - return kRGB_565_SkColorType; - - return kUnknown_SkColorType; -} - -// static -os::PixelAlpha SkiaSurface::asPixelAlpha(SkAlphaType at) -{ - switch(at) { - case SkAlphaType::kOpaque_SkAlphaType: - return os::PixelAlpha::kOpaque; - case SkAlphaType::kPremul_SkAlphaType: - return os::PixelAlpha::kPremultiplied; - case SkAlphaType::kUnpremul_SkAlphaType: - return os::PixelAlpha::kStraight; - default: - throw base::Exception("Unsupported alpha type"); - } -} - -// static -SkAlphaType SkiaSurface::asSkAlphaType(os::PixelAlpha pa) -{ - switch(pa) { - case os::PixelAlpha::kOpaque: - return SkAlphaType::kOpaque_SkAlphaType; - case os::PixelAlpha::kPremultiplied: - return SkAlphaType::kPremul_SkAlphaType; - case os::PixelAlpha::kStraight: - return SkAlphaType::kUnpremul_SkAlphaType; - } -} - gfx::Color SkiaSurface::getPixel(int x, int y) const { // Clip input to avoid crash on SkBitmap::getColor() diff --git a/os/skia/skia_surface.h b/os/skia/skia_surface.h index ee6142866..29e1f3648 100644 --- a/os/skia/skia_surface.h +++ b/os/skia/skia_surface.h @@ -117,9 +117,6 @@ class SkiaSurface final : public Surface { static SurfaceRef loadSurface(const char* filename); private: - static SkColorType deductSkColorType(const os::SurfaceFormatData& sf); - static os::PixelAlpha asPixelAlpha(SkAlphaType at); - static SkAlphaType asSkAlphaType(os::PixelAlpha pa); void skDrawSurface( const Surface* src, const gfx::Clip& clip, From 0a2537d83a30b8cce4aceaea3ad7c6f2128ea1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Wed, 8 May 2024 16:03:05 -0300 Subject: [PATCH 36/41] [osx] Remove duplicated code to create DragEvent --- os/dnd.h | 10 +++++----- os/osx/view.mm | 40 ++++++++++++++++------------------------ 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/os/dnd.h b/os/dnd.h index 72979beff..ff0fcdf5a 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -76,11 +76,11 @@ namespace os { DragEvent(os::Window* target, DropOperation supportedOperations, const gfx::Point& dragPosition, - DragDataProvider* dataProvider) + std::unique_ptr& dataProvider) : m_target(target) , m_supportedOperations(supportedOperations) , m_position(dragPosition) - , m_dataProvider(dataProvider) {} + , m_dataProvider(std::move(dataProvider)) {} // Destination window of the DragEvent. os::Window* target() const { return m_target; } @@ -88,7 +88,7 @@ namespace os { DropOperation supportedOperations() const { return m_supportedOperations; } bool acceptDrop() const { return m_acceptDrop; } const gfx::Point& position() { return m_position; } - DragDataProvider* dataProvider() { return m_dataProvider; } + DragDataProvider* dataProvider() { return m_dataProvider.get(); } // Sets what will be the outcome of dropping the dragged data when it is // accepted by the target window. Only one of the enum values should be passed, @@ -100,7 +100,7 @@ namespace os { bool sourceSupports(DropOperation op) { return static_cast(m_supportedOperations) & static_cast(op); - } + } private: os::Window* m_target = nullptr; @@ -109,7 +109,7 @@ namespace os { DropOperation m_supportedOperations; bool m_acceptDrop = false; gfx::Point m_position; - DragDataProvider* m_dataProvider; + std::unique_ptr m_dataProvider = nullptr; }; class DragTarget { diff --git a/os/osx/view.mm b/os/osx/view.mm index 7ba35f07e..b1fb95833 100644 --- a/os/osx/view.mm +++ b/os/osx/view.mm @@ -657,6 +657,17 @@ - (void)updateCurrentCursor } } +os::DragEvent newDragEvent(id sender) +{ + NSPasteboard* pasteboard = [sender draggingPasteboard]; + std::unique_ptr ddProvider = std::make_unique(pasteboard); + os::Window* window = [(WindowOSXObjc*)sender.draggingDestinationWindow impl]; + return os::DragEvent(window, + as_dropoperation([sender draggingSourceOperationMask]), + drag_position(sender), + ddProvider); +} + - (NSDragOperation)draggingEntered:(id)sender { WindowOSXObjc* target = (WindowOSXObjc*)sender.draggingDestinationWindow; @@ -664,13 +675,8 @@ - (NSDragOperation)draggingEntered:(id)sender if (!target || ![target impl]->hasDragTarget()) return NSDragOperationCopy; - NSPasteboard* pasteboard = [sender draggingPasteboard]; + os::DragEvent ev = newDragEvent(sender); os::Window* window = [target impl]; - auto ddProvider = std::make_unique(pasteboard); - os::DragEvent ev(window, - as_dropoperation([sender draggingSourceOperationMask]), - drag_position(sender), - ddProvider.get()); window->notifyDragEnter(ev); return as_nsdragoperation(ev.dropResult()); } @@ -683,14 +689,9 @@ - (NSDragOperation)draggingUpdated:(id)sender if (!target || ![target impl]->hasDragTarget()) return value; - NSPasteboard* pasteboard = [sender draggingPasteboard]; - os::Window* window = [target impl]; - auto ddProvider = std::make_unique(pasteboard); - os::DragEvent ev(window, - as_dropoperation([sender draggingSourceOperationMask]), - drag_position(sender), - ddProvider.get()); + os::DragEvent ev = newDragEvent(sender); ev.dropResult(as_dropoperation(value)); + os::Window* window = [target impl]; window->notifyDrag(ev); return as_nsdragoperation(ev.dropResult()); } @@ -702,13 +703,8 @@ - (void)draggingExited:(id)sender if (!target || ![target impl]->hasDragTarget()) return; - NSPasteboard* pasteboard = [sender draggingPasteboard]; + os::DragEvent ev = newDragEvent(sender); os::Window* window = [target impl]; - auto ddProvider = std::make_unique(pasteboard); - os::DragEvent ev(window, - as_dropoperation([sender draggingSourceOperationMask]), - drag_position(sender), - ddProvider.get()); window->notifyDragLeave(ev); } @@ -729,12 +725,8 @@ - (BOOL)performDragOperation:(id )sender return YES; } + os::DragEvent ev = newDragEvent(sender); os::Window* window = [target impl]; - auto ddProvider = std::make_unique(pasteboard); - os::DragEvent ev(window, - as_dropoperation([sender draggingSourceOperationMask]), - drag_position(sender), - ddProvider.get()); window->notifyDrop(ev); return ev.acceptDrop(); } From 97f5b85ebe468339f043b35cf293c36f042b8484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Wed, 8 May 2024 17:12:21 -0300 Subject: [PATCH 37/41] [win] Remove duplicated code to create DragEvent --- os/win/dnd.cpp | 51 +++++++++++++++++--------------------------------- os/win/dnd.h | 2 ++ 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 3f2dca47b..2f3627694 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -69,7 +69,7 @@ class GLock { operator T() { return m_data; } - T operator ->() { return m_data; } + T operator->() { return m_data; } bool operator==(std::nullptr_t) const { return m_data == nullptr; } bool operator!=(std::nullptr_t) const { return m_data != nullptr; } @@ -357,6 +357,18 @@ ULONG DragTargetAdapter::Release() return ref; } +DragEvent DragTargetAdapter::newDragEvent(POINTL* pt, DWORD* pdwEffect) +{ + if (pt) + m_position = drag_position((HWND)m_window->nativeHandle(), *pt); + + std::unique_ptr ddProvider = std::make_unique(m_data.get()); + return DragEvent(m_window, + as_dropoperation(*pdwEffect), + m_position, + ddProvider); +} + STDMETHODIMP DragTargetAdapter::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, @@ -369,17 +381,9 @@ STDMETHODIMP DragTargetAdapter::DragEnter(IDataObject* pDataObj, if (!m_data) return E_UNEXPECTED; - m_position = drag_position((HWND)m_window->nativeHandle(), pt); - auto ddProvider = std::make_unique(m_data.get()); - DragEvent ev(m_window, - as_dropoperation(*pdwEffect), - m_position, - ddProvider.get()); - + DragEvent ev = newDragEvent(&pt, pdwEffect); m_window->notifyDragEnter(ev); - *pdwEffect = as_dropeffect(ev.dropResult()); - return S_OK; } @@ -390,17 +394,9 @@ STDMETHODIMP DragTargetAdapter::DragOver(DWORD grfKeyState, if (!m_window->hasDragTarget()) return E_NOTIMPL; - m_position = drag_position((HWND)m_window->nativeHandle(), pt); - auto ddProvider = std::make_unique(m_data.get()); - DragEvent ev(m_window, - as_dropoperation(*pdwEffect), - m_position, - ddProvider.get()); - + DragEvent ev = newDragEvent(&pt, pdwEffect); m_window->notifyDrag(ev); - *pdwEffect = as_dropeffect(ev.dropResult()); - return S_OK; } @@ -409,13 +405,8 @@ STDMETHODIMP DragTargetAdapter::DragLeave(void) if (!m_window->hasDragTarget()) return E_NOTIMPL; - auto ddProvider = std::make_unique(m_data.get()); - os::DragEvent ev(m_window, - DropOperation::None, - m_position, - ddProvider.get()); + DragEvent ev = newDragEvent(nullptr, DROPEFFECT_NONE); m_window->notifyDragLeave(ev); - m_data.reset(); return S_OK; } @@ -432,19 +423,11 @@ STDMETHODIMP DragTargetAdapter::Drop(IDataObject* pDataObj, if (!m_data) return E_UNEXPECTED; - m_position = drag_position((HWND)m_window->nativeHandle(), pt); - auto ddProvider = std::make_unique(m_data.get()); - DragEvent ev(m_window, - as_dropoperation(*pdwEffect), - m_position, - ddProvider.get()); - + DragEvent ev = newDragEvent(&pt, pdwEffect); m_window->notifyDrop(ev); - m_data = nullptr; *pdwEffect = as_dropeffect(ev.dropResult()); return S_OK; } - } // namespase os diff --git a/os/win/dnd.h b/os/win/dnd.h index 7834e8232..f5859aae2 100644 --- a/os/win/dnd.h +++ b/os/win/dnd.h @@ -56,6 +56,8 @@ namespace os { DWORD* pdwEffect) override; private: + DragEvent DragTargetAdapter::newDragEvent(POINTL* pt, DWORD* pdwEffect); + ULONG m_ref = 0; Window* m_window = nullptr; // Pointer to data being dragged From 26533b4438ffc509cf95f32955354965c16eb95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Wed, 8 May 2024 17:33:23 -0300 Subject: [PATCH 38/41] [win] Take into account the window scale when calculating the drag position --- os/win/dnd.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 2f3627694..4f3b8002f 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -46,13 +46,6 @@ os::DropOperation as_dropoperation(DWORD pdwEffect) return static_cast(op); } - -gfx::Point drag_position(HWND hwnd, POINTL& pt) -{ - ScreenToClient(hwnd, (LPPOINT) &pt); - return gfx::Point(pt.x, pt.y); -} - // HGLOBAL Locking/Unlocking wrapper template class GLock { @@ -360,7 +353,8 @@ ULONG DragTargetAdapter::Release() DragEvent DragTargetAdapter::newDragEvent(POINTL* pt, DWORD* pdwEffect) { if (pt) - m_position = drag_position((HWND)m_window->nativeHandle(), *pt); + // Get drag position + m_position = m_window->pointFromScreen(gfx::Point(pt->x, pt->y)); std::unique_ptr ddProvider = std::make_unique(m_data.get()); return DragEvent(m_window, From 53de63b5811b934d09e1b1fd3a2cbdb0f68d265a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Thu, 9 May 2024 09:16:48 -0300 Subject: [PATCH 39/41] Change sourceSupports behavior to return true when it matches a set of flags instead of any of the specified flags --- os/dnd.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/os/dnd.h b/os/dnd.h index ff0fcdf5a..557c2fbad 100644 --- a/os/dnd.h +++ b/os/dnd.h @@ -49,7 +49,7 @@ namespace os { Copy = 1, Move = 2, Link = 4, - Any = Copy | Move | Link, + All = Copy | Move | Link, }; // Types of representations supported for each DragDataItem. @@ -99,7 +99,8 @@ namespace os { void acceptDrop(bool value) { m_acceptDrop = value; } bool sourceSupports(DropOperation op) { - return static_cast(m_supportedOperations) & static_cast(op); + return (static_cast(m_supportedOperations) & static_cast(op)) + == static_cast(op); } private: From 228d5550d2cda8edf7c07cbe0b45a99697e322cd Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 30 May 2024 10:55:05 -0300 Subject: [PATCH 40/41] Minor changes in drag_and_drop example The most important change here is clearing the data from the windowData about types that weren't dropped in the last action, to avoid mixing dropped data from previous actions and only show the last dropped data. --- examples/drag_and_drop.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/drag_and_drop.cpp b/examples/drag_and_drop.cpp index f368db063..a6cac2528 100644 --- a/examples/drag_and_drop.cpp +++ b/examples/drag_and_drop.cpp @@ -65,12 +65,20 @@ class DragTarget : public os::DragTarget { if (ev.acceptDrop()) { if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) windowData.paths = ev.dataProvider()->getPaths(); + else + windowData.paths.clear(); + #if CLIP_ENABLE_IMAGE if (ev.dataProvider()->contains(os::DragDataItemType::Image)) windowData.image = ev.dataProvider()->getImage(); + else + windowData.image.reset(); #endif + if (ev.dataProvider()->contains(os::DragDataItemType::Url)) windowData.url = ev.dataProvider()->getUrl(); + else + windowData.url.clear(); } redraw_window(ev.target()); @@ -167,7 +175,7 @@ static os::WindowRef create_window(os::DragTarget& dragTarget) os::WindowRef newWindow = os::instance()->makeWindow(spec); newWindow->setCursor(os::NativeCursor::Arrow); - newWindow->setTitle("Drag & Drop example"); + newWindow->setTitle("Drag & Drop"); newWindow->setDragTarget(&dragTarget); windowData.dropZone = gfx::Rect(spec.frame().w-64-12, 12, 64, 64); @@ -196,6 +204,14 @@ int app_main(int argc, char* argv[]) switch (ev.type()) { + case os::Event::KeyDown: + switch (ev.scancode()) { + case os::kKeyEsc: + running = false; + break; + } + break; + case os::Event::CloseApp: case os::Event::CloseWindow: running = false; From 4a2e2bb3d91ea5f00d2aac17477bc06434a89e14 Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 30 May 2024 11:18:32 -0300 Subject: [PATCH 41/41] Update clip module --- clip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clip b/clip index 7fa83b2f8..8c0c3822a 160000 --- a/clip +++ b/clip @@ -1 +1 @@ -Subproject commit 7fa83b2f817a7da49e8ea27f6dbe2775ead13ddf +Subproject commit 8c0c3822ac49c3ac3bee7cd9ac7dfe8759d74cda