Skip to content

Commit

Permalink
Introduce drag and drop handling
Browse files Browse the repository at this point in the history
  • Loading branch information
martincapello committed Mar 20, 2024
1 parent 7c52147 commit c700c34
Show file tree
Hide file tree
Showing 15 changed files with 801 additions and 14 deletions.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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(helloworld GUI)
laf_add_example(listfonts CONSOLE)
Expand Down
211 changes: 211 additions & 0 deletions examples/drag_and_drop.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// 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 <algorithm>
#include <cstdarg>
#include <cstdio>
#include <map>
#include <memory>
#include <vector>

using Boxes = std::vector<gfx::Rect>;

struct WindowData {
bool dragEnter;
bool dragLeave;
int drag;
base::paths paths;
std::unique_ptr<os::Surface> 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();
else if (ev.dataProvider()->contains(os::DragDataItemType::Image))
windowData.image = ev.dataProvider()->getImage();
}

redraw_window(ev.target());
ev.target()->invalidate();
}
};

static os::WindowRef window;

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;

// Create two windows that can act as Drag and Drop target and/or source.
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);
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;
}
106 changes: 106 additions & 0 deletions os/dnd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 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 <memory>

#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 DragDataItemType {
Paths,
Image
};

// Interface to get dragged data from the platform's implementation.
class DragDataProvider {
public:
virtual ~DragDataProvider() {}
virtual base::paths getPaths() = 0;
virtual std::unique_ptr<os::Surface> getImage() = 0;
virtual bool contains(DragDataItemType type) { return false; }
};

class DragEvent {
public:
DragEvent(os::Window* target,
DropOperation supportedOperations,
const gfx::Point& dragPosition,
std::unique_ptr<DragDataProvider> dataProvider)
: m_target(target)
, m_supportedOperations(supportedOperations)
, m_position(dragPosition)
, m_dataProvider(std::move(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.get(); }

// Sets what will be the outcome of dropping the dragged data when it is
// accepted by the target window.
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;
std::unique_ptr<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) {}
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
3 changes: 3 additions & 0 deletions os/none/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class NoneSystem : public System {
Ref<Window> makeWindow(const WindowSpec& spec) override { return nullptr; }
Ref<Surface> makeSurface(int width, int height,
const os::ColorSpaceRef& colorSpace) override { return nullptr; }
Ref<Surface> makeSurface(int width, int height,
const os::SurfaceFormatData& sf,
const unsigned char* data = nullptr) override { return nullptr; }
Ref<Surface> makeRgbaSurface(int width, int height,
const os::ColorSpaceRef& colorSpace) override { return nullptr; }
Ref<Surface> loadSurface(const char* filename) override { return nullptr; }
Expand Down
1 change: 1 addition & 0 deletions os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading

0 comments on commit c700c34

Please sign in to comment.