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 8, 2024
1 parent 7c52147 commit 0dafb45
Show file tree
Hide file tree
Showing 9 changed files with 582 additions and 6 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
239 changes: 239 additions & 0 deletions examples/drag_and_drop.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
// 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/rect.h"
#include "gfx/rgb.h"
#include "os/dnd.h"
#include "os/draw_text.h"
#include "os/os.h"

#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <map>
#include <memory>
#include <vector>

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

struct WindowData {
Boxes boxes;
bool dragEnter;
bool dragLeave;
int drag;
bool acceptDrop = true;
base::paths paths;
};

static std::map<const os::Window*, WindowData> windowData;

static void redraw_window(os::Window* window);

class DragTarget : public os::DragTarget {
public:
DragTarget(const os::WindowRef& window) : m_window(window) {

}
void dragEnter(os::DragEvent& ev) override {
auto& data = windowData[ev.target()];

if (!data.acceptDrop || !ev.sourceSupports(os::DropOperation::Copy))
ev.dropResult(os::DropOperation::None);
else if (ev.sourceSupports(os::DropOperation::Copy))
ev.dropResult(os::DropOperation::Copy);

data.dragEnter = true;
data.dragLeave = false;
data.drag = 0;
redraw_window(ev.target());
ev.target()->invalidate();
}
void dragLeave(os::DragEvent& ev) override {
auto& data = windowData[ev.target()];
data.dragEnter = false;
data.dragLeave = true;
redraw_window(ev.target());
ev.target()->invalidate();
}
void drag(os::DragEvent& ev) override {
++windowData[ev.target()].drag;
redraw_window(ev.target());
ev.target()->invalidate();
}
void drop(os::DragEvent& ev) override {
auto& data = windowData[ev.target()];
data.dragEnter = false;
data.dragLeave = false;
if (data.acceptDrop)
data.paths = ev.data()->getItems<os::DragDataItemType::Paths>()[0];
ev.acceptDrop(data.acceptDrop);
redraw_window(ev.target());
ev.target()->invalidate();
}

private:
os::WindowRef m_window;
};

class DragSource : public os::DragSource {
public:

};

static os::WindowRef windowA;
static os::WindowRef windowB;

const char* lines[] = {
"T: Enable/disable acting as drag and drop target",
"S: Enable/disable acting as drag and drop source",
"A: Accept drop switch",
};

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;

for (auto line : lines) {
y += 12;
os::draw_text(s, nullptr, line, gfx::Point(0, y), &paint);
}

const WindowData& data = windowData[window];
y += 12;
std::snprintf(buf, sizeof(buf), "Drag Enter = %s", data.dragEnter ? "true" : "false");
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
y += 12;
std::snprintf(buf, sizeof(buf), "Drag = %d", data.drag);
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
y += 12;
std::snprintf(buf, sizeof(buf), "Drag Leave = %s", data.dragLeave ? "true" : "false");
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
y += 12;
std::snprintf(buf, sizeof(buf), "Accept drop = %s", data.acceptDrop ? "true" : "false");
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);

if (!data.paths.empty()) {
y += 12;
std::snprintf(buf, sizeof(buf), "Paths = %lu", data.paths.size());
os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
for (const auto& path : data.paths) {
y += 12;
std::snprintf(buf, sizeof(buf), "%s", path.c_str());
os::draw_text(s, nullptr, buf, gfx::Point(12, y), &paint);
}
}

paint.style(os::Paint::Style::Stroke);
s->drawRect(window->bounds(), paint);

paint.color(gfx::rgba(100, 255, 100));
for (const auto& box : data.boxes) {
paint.style(os::Paint::Style::Stroke);
s->drawRect(box, paint);
paint.style(os::Paint::Style::Fill);
os::draw_text(s, nullptr, "Drag me!", box.center(), &paint, os::TextAlign::Center);
}
}

static void create_box(const os::Window* window, const gfx::Rect& box)
{
windowData[window].boxes.push_back(box);
}

static os::WindowRef create_window(const std::string& title,
const os::WindowSpec& spec,
const std::vector<gfx::Rect>& initialBoxes)
{
os::WindowRef newWindow = os::instance()->makeWindow(spec);
newWindow->setCursor(os::NativeCursor::Arrow);
newWindow->setTitle(title);
newWindow->setVisible(true);
newWindow->setDragTarget(std::make_unique<DragTarget>(newWindow));
for (const auto& box : initialBoxes) {
create_box(newWindow.get(), box);
}

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;
spec.titled(true);
spec.position(os::WindowSpec::Position::Frame);
auto frame = screen->workarea()/2;
spec.frame(frame);
spec.screen(screen);
windowA = create_window("A", spec, {{32, frame.h - 64 - 40, 64, 64}});
frame.offset(frame.w, 0);
spec.frame(frame);
windowB = create_window("B", spec, {});

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;

case os::Event::KeyDown:
switch (ev.scancode()) {

case os::kKeyT:
break;

case os::kKeyS:
break;

case os::kKeyA:
windowData[ev.window().get()].acceptDrop = !windowData[ev.window().get()].acceptDrop;
redraw_window(ev.window().get());
ev.window()->invalidate();
break;
}
break;

default:
// Do nothing
break;
}
}

return 0;
}
Loading

0 comments on commit 0dafb45

Please sign in to comment.