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:
David Capello 2018-12-21 12:22:25 -03:00
parent 3fdde68bdb
commit b6d9156013
5 changed files with 203 additions and 3 deletions

View 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

View File

@ -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:

View File

@ -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

View File

@ -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
// ===============================================================

View File

@ -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