Skip to content

Commit 0dafb45

Browse files
committed
Introduce drag and drop handling
1 parent 7c52147 commit 0dafb45

File tree

9 files changed

+582
-6
lines changed

9 files changed

+582
-6
lines changed

examples/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ if(LAF_BACKEND STREQUAL "skia")
1616
laf_add_example(base64 CONSOLE)
1717
laf_add_example(complextextlayout GUI)
1818
laf_add_example(custom_window GUI)
19+
laf_add_example(drag_and_drop GUI)
1920
laf_add_example(floating_window GUI)
2021
laf_add_example(helloworld GUI)
2122
laf_add_example(listfonts CONSOLE)

examples/drag_and_drop.cpp

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// LAF Library
2+
// Copyright (c) 2024 Igara Studio S.A.
3+
//
4+
// This file is released under the terms of the MIT license.
5+
// Read LICENSE.txt for more information.
6+
7+
8+
#include "base/paths.h"
9+
#include "gfx/hsv.h"
10+
#include "gfx/rect.h"
11+
#include "gfx/rgb.h"
12+
#include "os/dnd.h"
13+
#include "os/draw_text.h"
14+
#include "os/os.h"
15+
16+
#include <algorithm>
17+
#include <cstdarg>
18+
#include <cstdio>
19+
#include <map>
20+
#include <memory>
21+
#include <vector>
22+
23+
using Boxes = std::vector<gfx::Rect>;
24+
25+
struct WindowData {
26+
Boxes boxes;
27+
bool dragEnter;
28+
bool dragLeave;
29+
int drag;
30+
bool acceptDrop = true;
31+
base::paths paths;
32+
};
33+
34+
static std::map<const os::Window*, WindowData> windowData;
35+
36+
static void redraw_window(os::Window* window);
37+
38+
class DragTarget : public os::DragTarget {
39+
public:
40+
DragTarget(const os::WindowRef& window) : m_window(window) {
41+
42+
}
43+
void dragEnter(os::DragEvent& ev) override {
44+
auto& data = windowData[ev.target()];
45+
46+
if (!data.acceptDrop || !ev.sourceSupports(os::DropOperation::Copy))
47+
ev.dropResult(os::DropOperation::None);
48+
else if (ev.sourceSupports(os::DropOperation::Copy))
49+
ev.dropResult(os::DropOperation::Copy);
50+
51+
data.dragEnter = true;
52+
data.dragLeave = false;
53+
data.drag = 0;
54+
redraw_window(ev.target());
55+
ev.target()->invalidate();
56+
}
57+
void dragLeave(os::DragEvent& ev) override {
58+
auto& data = windowData[ev.target()];
59+
data.dragEnter = false;
60+
data.dragLeave = true;
61+
redraw_window(ev.target());
62+
ev.target()->invalidate();
63+
}
64+
void drag(os::DragEvent& ev) override {
65+
++windowData[ev.target()].drag;
66+
redraw_window(ev.target());
67+
ev.target()->invalidate();
68+
}
69+
void drop(os::DragEvent& ev) override {
70+
auto& data = windowData[ev.target()];
71+
data.dragEnter = false;
72+
data.dragLeave = false;
73+
if (data.acceptDrop)
74+
data.paths = ev.data()->getItems<os::DragDataItemType::Paths>()[0];
75+
ev.acceptDrop(data.acceptDrop);
76+
redraw_window(ev.target());
77+
ev.target()->invalidate();
78+
}
79+
80+
private:
81+
os::WindowRef m_window;
82+
};
83+
84+
class DragSource : public os::DragSource {
85+
public:
86+
87+
};
88+
89+
static os::WindowRef windowA;
90+
static os::WindowRef windowB;
91+
92+
const char* lines[] = {
93+
"T: Enable/disable acting as drag and drop target",
94+
"S: Enable/disable acting as drag and drop source",
95+
"A: Accept drop switch",
96+
};
97+
98+
static void redraw_window(os::Window* window)
99+
{
100+
os::Surface* s = window->surface();
101+
os::Paint paint;
102+
paint.color(gfx::rgba(0, 0, 0));
103+
s->drawRect(window->bounds(), paint);
104+
105+
paint.color(gfx::rgba(255, 255, 255));
106+
107+
char buf[256];
108+
int y = 12;
109+
110+
for (auto line : lines) {
111+
y += 12;
112+
os::draw_text(s, nullptr, line, gfx::Point(0, y), &paint);
113+
}
114+
115+
const WindowData& data = windowData[window];
116+
y += 12;
117+
std::snprintf(buf, sizeof(buf), "Drag Enter = %s", data.dragEnter ? "true" : "false");
118+
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
119+
y += 12;
120+
std::snprintf(buf, sizeof(buf), "Drag = %d", data.drag);
121+
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
122+
y += 12;
123+
std::snprintf(buf, sizeof(buf), "Drag Leave = %s", data.dragLeave ? "true" : "false");
124+
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
125+
y += 12;
126+
std::snprintf(buf, sizeof(buf), "Accept drop = %s", data.acceptDrop ? "true" : "false");
127+
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
128+
129+
if (!data.paths.empty()) {
130+
y += 12;
131+
std::snprintf(buf, sizeof(buf), "Paths = %lu", data.paths.size());
132+
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
133+
for (const auto& path : data.paths) {
134+
y += 12;
135+
std::snprintf(buf, sizeof(buf), "%s", path.c_str());
136+
os::draw_text(s, nullptr, buf, gfx::Point(12, y), &paint);
137+
}
138+
}
139+
140+
paint.style(os::Paint::Style::Stroke);
141+
s->drawRect(window->bounds(), paint);
142+
143+
paint.color(gfx::rgba(100, 255, 100));
144+
for (const auto& box : data.boxes) {
145+
paint.style(os::Paint::Style::Stroke);
146+
s->drawRect(box, paint);
147+
paint.style(os::Paint::Style::Fill);
148+
os::draw_text(s, nullptr, "Drag me!", box.center(), &paint, os::TextAlign::Center);
149+
}
150+
}
151+
152+
static void create_box(const os::Window* window, const gfx::Rect& box)
153+
{
154+
windowData[window].boxes.push_back(box);
155+
}
156+
157+
static os::WindowRef create_window(const std::string& title,
158+
const os::WindowSpec& spec,
159+
const std::vector<gfx::Rect>& initialBoxes)
160+
{
161+
os::WindowRef newWindow = os::instance()->makeWindow(spec);
162+
newWindow->setCursor(os::NativeCursor::Arrow);
163+
newWindow->setTitle(title);
164+
newWindow->setVisible(true);
165+
newWindow->setDragTarget(std::make_unique<DragTarget>(newWindow));
166+
for (const auto& box : initialBoxes) {
167+
create_box(newWindow.get(), box);
168+
}
169+
170+
redraw_window(newWindow.get());
171+
return newWindow;
172+
}
173+
174+
int app_main(int argc, char* argv[])
175+
{
176+
auto system = os::make_system();
177+
system->setAppMode(os::AppMode::GUI);
178+
system->handleWindowResize = redraw_window;
179+
180+
// Create two windows that can act as Drag and Drop target and/or source.
181+
auto screen = system->mainScreen();
182+
os::WindowSpec spec;
183+
spec.titled(true);
184+
spec.position(os::WindowSpec::Position::Frame);
185+
auto frame = screen->workarea()/2;
186+
spec.frame(frame);
187+
spec.screen(screen);
188+
windowA = create_window("A", spec, {{32, frame.h - 64 - 40, 64, 64}});
189+
frame.offset(frame.w, 0);
190+
spec.frame(frame);
191+
windowB = create_window("B", spec, {});
192+
193+
bool running = true;
194+
195+
system->finishLaunching();
196+
system->activateApp();
197+
198+
os::EventQueue* queue = system->eventQueue();
199+
os::Event ev;
200+
while (running) {
201+
queue->getEvent(ev);
202+
203+
switch (ev.type()) {
204+
205+
case os::Event::CloseApp:
206+
case os::Event::CloseWindow:
207+
running = false;
208+
break;
209+
210+
case os::Event::ResizeWindow:
211+
redraw_window(ev.window().get());
212+
ev.window()->invalidate();
213+
break;
214+
215+
case os::Event::KeyDown:
216+
switch (ev.scancode()) {
217+
218+
case os::kKeyT:
219+
break;
220+
221+
case os::kKeyS:
222+
break;
223+
224+
case os::kKeyA:
225+
windowData[ev.window().get()].acceptDrop = !windowData[ev.window().get()].acceptDrop;
226+
redraw_window(ev.window().get());
227+
ev.window()->invalidate();
228+
break;
229+
}
230+
break;
231+
232+
default:
233+
// Do nothing
234+
break;
235+
}
236+
}
237+
238+
return 0;
239+
}

0 commit comments

Comments
 (0)