-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7c52147
commit c700c34
Showing
15 changed files
with
801 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.