mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-14 09:41:19 +00:00
Add support to drag and drop recent file items
The items are still not persisted/synchronized with the .ini file.
This commit is contained in:
parent
3fdde68bdb
commit
b6d9156013
165
src/app/ui/draggable_widget.h
Normal file
165
src/app/ui/draggable_widget.h
Normal file
@ -0,0 +1,165 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_DRAGGABLE_WIDGET_H_INCLUDED
|
||||
#define APP_UI_DRAGGABLE_WIDGET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/surface.h"
|
||||
#include "os/system.h"
|
||||
#include "ui/graphics.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/overlay.h"
|
||||
#include "ui/overlay_manager.h"
|
||||
#include "ui/paint_event.h"
|
||||
#include "ui/system.h"
|
||||
#include "ui/view.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
template<typename Base>
|
||||
class DraggableWidget : public Base {
|
||||
public:
|
||||
template<typename...Args>
|
||||
DraggableWidget(Args...args) : Base(args...) { }
|
||||
|
||||
bool onProcessMessage(ui::Message* msg) override {
|
||||
switch (msg->type()) {
|
||||
|
||||
case ui::kSetCursorMessage:
|
||||
if (m_floatingOverlay) {
|
||||
ui::set_mouse_cursor(ui::kMoveCursor);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case ui::kMouseDownMessage: {
|
||||
const bool wasCaptured = this->hasCapture();
|
||||
const bool result = Base::onProcessMessage(msg);
|
||||
|
||||
if (!wasCaptured && this->hasCapture()) {
|
||||
const ui::MouseMessage* mouseMsg = static_cast<ui::MouseMessage*>(msg);
|
||||
const gfx::Point mousePos = mouseMsg->position();
|
||||
m_dragMousePos = mousePos;
|
||||
m_floatingOffset = mouseMsg->position() - this->bounds().origin();
|
||||
m_createFloatingOverlay = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
case ui::kMouseMoveMessage: {
|
||||
const ui::MouseMessage* mouseMsg = static_cast<ui::MouseMessage*>(msg);
|
||||
const gfx::Point mousePos = mouseMsg->position();
|
||||
|
||||
if (this->hasCapture() && m_createFloatingOverlay) {
|
||||
if (this->manager()->pick(mousePos) != this) {
|
||||
m_createFloatingOverlay = false;
|
||||
if (!m_floatingOverlay)
|
||||
createFloatingOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_floatingOverlay) {
|
||||
m_floatingOverlay->moveOverlay(mousePos - m_floatingOffset);
|
||||
onReorderWidgets(mousePos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ui::kMouseUpMessage: {
|
||||
m_wasDragged = (this->hasCapture() && m_floatingOverlay);
|
||||
const bool result = Base::onProcessMessage(msg);
|
||||
m_wasDragged = false;
|
||||
|
||||
if (!this->hasCapture()) {
|
||||
if (m_floatingOverlay) {
|
||||
destroyFloatingOverlay();
|
||||
ASSERT(!m_createFloatingOverlay);
|
||||
}
|
||||
else if (m_createFloatingOverlay)
|
||||
m_createFloatingOverlay = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
return Base::onProcessMessage(msg);
|
||||
}
|
||||
|
||||
bool wasDragged() const {
|
||||
return m_wasDragged;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void createFloatingOverlay() {
|
||||
ASSERT(!m_floatingOverlay);
|
||||
|
||||
gfx::Size sz = getFloatingOverlaySize();
|
||||
os::Surface* surface = os::instance()->createRgbaSurface(sz.w, sz.h);
|
||||
|
||||
{
|
||||
os::SurfaceLock lock(surface);
|
||||
surface->fillRect(gfx::rgba(0, 0, 0, 0),
|
||||
gfx::Rect(0, 0, surface->width(), surface->height()));
|
||||
}
|
||||
{
|
||||
ui::Graphics g(surface, 0, 0);
|
||||
g.setFont(this->font());
|
||||
drawFloatingOverlay(g);
|
||||
}
|
||||
|
||||
m_floatingOverlay.reset(new ui::Overlay(surface, gfx::Point(),
|
||||
ui::Overlay::MouseZOrder-1));
|
||||
ui::OverlayManager::instance()->addOverlay(m_floatingOverlay.get());
|
||||
}
|
||||
|
||||
void destroyFloatingOverlay() {
|
||||
ui::OverlayManager::instance()->removeOverlay(m_floatingOverlay.get());
|
||||
m_floatingOverlay.reset();
|
||||
}
|
||||
|
||||
gfx::Size getFloatingOverlaySize() {
|
||||
auto view = ui::View::getView(this);
|
||||
if (!view)
|
||||
view = ui::View::getView(this->parent());
|
||||
if (view)
|
||||
return (view->viewportBounds() & this->bounds()).size();
|
||||
else
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void drawFloatingOverlay(ui::Graphics& g) {
|
||||
ui::PaintEvent ev(this, &g);
|
||||
this->onPaint(ev);
|
||||
}
|
||||
|
||||
virtual void onReorderWidgets(const gfx::Point& mousePos) = 0;
|
||||
|
||||
// True if we should create the floating overlay after leaving the
|
||||
// widget bounds.
|
||||
bool m_createFloatingOverlay = false;
|
||||
|
||||
// True when the mouse button is released (drop operation) and we've
|
||||
// dragged the widget to other position. Can be used to avoid
|
||||
// triggering the default click operation by derived classes when
|
||||
// we've dragged the widget.
|
||||
bool m_wasDragged = false;
|
||||
|
||||
// Initial mouse position when we start the dragging process.
|
||||
gfx::Point m_dragMousePos;
|
||||
|
||||
// Overlay used to show the floating widget (this overlay floats
|
||||
// next to the mouse cursor).
|
||||
std::unique_ptr<ui::Overlay> m_floatingOverlay;
|
||||
|
||||
// Relative mouse position between the widget and the overlay.
|
||||
gfx::Point m_floatingOffset;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -16,6 +17,7 @@
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/recent_files.h"
|
||||
#include "app/ui/draggable_widget.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/bind.h"
|
||||
@ -38,10 +40,10 @@ using namespace skin;
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// RecentFileItem
|
||||
|
||||
class RecentFileItem : public LinkLabel {
|
||||
class RecentFileItem : public DraggableWidget<LinkLabel> {
|
||||
public:
|
||||
RecentFileItem(const std::string& file)
|
||||
: LinkLabel("")
|
||||
: DraggableWidget<LinkLabel>("")
|
||||
, m_fullpath(file)
|
||||
, m_name(base::get_file_name(file))
|
||||
, m_path(base::get_file_path(file)) {
|
||||
@ -89,7 +91,17 @@ protected:
|
||||
}
|
||||
|
||||
void onClick() override {
|
||||
static_cast<RecentListBox*>(parent())->onClick(m_fullpath);
|
||||
if (!wasDragged())
|
||||
static_cast<RecentListBox*>(parent())->onClick(m_fullpath);
|
||||
}
|
||||
|
||||
void onReorderWidgets(const gfx::Point& mousePos) override {
|
||||
auto parent = this->parent();
|
||||
auto other = manager()->pick(mousePos);
|
||||
if (other && other != this && other->parent() == parent) {
|
||||
parent->moveChildTo(this, other);
|
||||
parent->layout();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -13,6 +14,7 @@
|
||||
#include "os/display.h"
|
||||
#include "os/surface.h"
|
||||
#include "os/system.h"
|
||||
#include "ui/overlay_manager.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -27,6 +29,11 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
|
||||
if (!display)
|
||||
return;
|
||||
|
||||
auto overlays = ui::OverlayManager::instance();
|
||||
gfx::Rect bounds = region.bounds();
|
||||
bounds |= gfx::Rect(bounds).offset(dx, dy);
|
||||
overlays->restoreOverlappedAreas(bounds);
|
||||
|
||||
os::Surface* surface = display->getSurface();
|
||||
os::SurfaceLock lock(surface);
|
||||
std::size_t nrects = region.size();
|
||||
@ -83,6 +90,8 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
|
||||
manager->dirtyRect(rc);
|
||||
}
|
||||
}
|
||||
|
||||
overlays->drawOverlays();
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@ -577,6 +577,19 @@ void Widget::insertChild(int index, Widget* child)
|
||||
child->m_parent = this;
|
||||
}
|
||||
|
||||
void Widget::moveChildTo(Widget* thisChild, Widget* toThisPosition)
|
||||
{
|
||||
auto itA = std::find(m_children.begin(), m_children.end(), thisChild);
|
||||
auto itB = std::find(m_children.begin(), m_children.end(), toThisPosition);
|
||||
if (itA == m_children.end()) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
int index = itB - m_children.begin();
|
||||
m_children.erase(itA);
|
||||
m_children.insert(m_children.begin() + index, thisChild);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// LAYOUT & CONSTRAINT
|
||||
// ===============================================================
|
||||
|
@ -212,6 +212,7 @@ namespace ui {
|
||||
void removeAllChildren();
|
||||
void replaceChild(Widget* oldChild, Widget* newChild);
|
||||
void insertChild(int index, Widget* child);
|
||||
void moveChildTo(Widget* thisChild, Widget* toThisPosition);
|
||||
|
||||
// ===============================================================
|
||||
// LAYOUT & CONSTRAINT
|
||||
|
Loading…
x
Reference in New Issue
Block a user