mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-20 18:40:57 +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
|
// Aseprite
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -16,6 +17,7 @@
|
|||||||
#include "app/i18n/strings.h"
|
#include "app/i18n/strings.h"
|
||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
#include "app/recent_files.h"
|
#include "app/recent_files.h"
|
||||||
|
#include "app/ui/draggable_widget.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "app/ui_context.h"
|
#include "app/ui_context.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
@ -38,10 +40,10 @@ using namespace skin;
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// RecentFileItem
|
// RecentFileItem
|
||||||
|
|
||||||
class RecentFileItem : public LinkLabel {
|
class RecentFileItem : public DraggableWidget<LinkLabel> {
|
||||||
public:
|
public:
|
||||||
RecentFileItem(const std::string& file)
|
RecentFileItem(const std::string& file)
|
||||||
: LinkLabel("")
|
: DraggableWidget<LinkLabel>("")
|
||||||
, m_fullpath(file)
|
, m_fullpath(file)
|
||||||
, m_name(base::get_file_name(file))
|
, m_name(base::get_file_name(file))
|
||||||
, m_path(base::get_file_path(file)) {
|
, m_path(base::get_file_path(file)) {
|
||||||
@ -89,9 +91,19 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onClick() override {
|
void onClick() override {
|
||||||
|
if (!wasDragged())
|
||||||
static_cast<RecentListBox*>(parent())->onClick(m_fullpath);
|
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:
|
private:
|
||||||
std::string m_fullpath;
|
std::string m_fullpath;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite UI Library
|
// Aseprite UI Library
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -13,6 +14,7 @@
|
|||||||
#include "os/display.h"
|
#include "os/display.h"
|
||||||
#include "os/surface.h"
|
#include "os/surface.h"
|
||||||
#include "os/system.h"
|
#include "os/system.h"
|
||||||
|
#include "ui/overlay_manager.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -27,6 +29,11 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
|
|||||||
if (!display)
|
if (!display)
|
||||||
return;
|
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::Surface* surface = display->getSurface();
|
||||||
os::SurfaceLock lock(surface);
|
os::SurfaceLock lock(surface);
|
||||||
std::size_t nrects = region.size();
|
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);
|
manager->dirtyRect(rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlays->drawOverlays();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
@ -577,6 +577,19 @@ void Widget::insertChild(int index, Widget* child)
|
|||||||
child->m_parent = this;
|
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
|
// LAYOUT & CONSTRAINT
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
|
@ -212,6 +212,7 @@ namespace ui {
|
|||||||
void removeAllChildren();
|
void removeAllChildren();
|
||||||
void replaceChild(Widget* oldChild, Widget* newChild);
|
void replaceChild(Widget* oldChild, Widget* newChild);
|
||||||
void insertChild(int index, Widget* child);
|
void insertChild(int index, Widget* child);
|
||||||
|
void moveChildTo(Widget* thisChild, Widget* toThisPosition);
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// LAYOUT & CONSTRAINT
|
// LAYOUT & CONSTRAINT
|
||||||
|
Loading…
x
Reference in New Issue
Block a user