From b6d915601342a4a6627c5ef7b98c7fbb3962e6d3 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 21 Dec 2018 12:22:25 -0300 Subject: [PATCH] Add support to drag and drop recent file items The items are still not persisted/synchronized with the .ini file. --- src/app/ui/draggable_widget.h | 165 ++++++++++++++++++++++++++++++++++ src/app/ui/recent_listbox.cpp | 18 +++- src/ui/move_region.cpp | 9 ++ src/ui/widget.cpp | 13 +++ src/ui/widget.h | 1 + 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 src/app/ui/draggable_widget.h diff --git a/src/app/ui/draggable_widget.h b/src/app/ui/draggable_widget.h new file mode 100644 index 000000000..74e662493 --- /dev/null +++ b/src/app/ui/draggable_widget.h @@ -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 +class DraggableWidget : public Base { +public: + template + 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(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(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 m_floatingOverlay; + + // Relative mouse position between the widget and the overlay. + gfx::Point m_floatingOffset; +}; + +} // namespace app + +#endif diff --git a/src/app/ui/recent_listbox.cpp b/src/app/ui/recent_listbox.cpp index 6f6a150ad..370dcb0ef 100644 --- a/src/app/ui/recent_listbox.cpp +++ b/src/app/ui/recent_listbox.cpp @@ -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 { public: RecentFileItem(const std::string& file) - : LinkLabel("") + : DraggableWidget("") , 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(parent())->onClick(m_fullpath); + if (!wasDragged()) + static_cast(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: diff --git a/src/ui/move_region.cpp b/src/ui/move_region.cpp index 60c1026b6..b88b8ac9e 100644 --- a/src/ui/move_region.cpp +++ b/src/ui/move_region.cpp @@ -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 @@ -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 diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp index 0ad43ed7c..f48b77254 100644 --- a/src/ui/widget.cpp +++ b/src/ui/widget.cpp @@ -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 // =============================================================== diff --git a/src/ui/widget.h b/src/ui/widget.h index 7e4dd1aea..04ed00d1c 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -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