mirror of https://github.com/aseprite/aseprite.git
Show handles in dockable areas to drag-and-drop them
This commit is contained in:
parent
d0457cc1f4
commit
d9415fdf1b
|
@ -104,6 +104,7 @@ public:
|
|||
|
||||
// Dockable impl
|
||||
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||
int dockHandleSide() const override { return ui::LEFT; }
|
||||
|
||||
// Signals
|
||||
obs::signal<void()> BrushChange;
|
||||
|
|
|
@ -99,6 +99,22 @@ Dock::Dock()
|
|||
initTheme();
|
||||
}
|
||||
|
||||
void Dock::setCustomizing(bool enable, bool doLayout)
|
||||
{
|
||||
m_customizing = enable;
|
||||
|
||||
for (int i = 0; i < kSides; ++i) {
|
||||
auto child = m_sides[i];
|
||||
if (!child)
|
||||
continue;
|
||||
else if (auto subdock = dynamic_cast<Dock*>(child))
|
||||
subdock->setCustomizing(enable, false);
|
||||
}
|
||||
|
||||
if (doLayout)
|
||||
layout();
|
||||
}
|
||||
|
||||
void Dock::resetDocks()
|
||||
{
|
||||
for (int i = 0; i < kSides; ++i) {
|
||||
|
@ -166,6 +182,7 @@ void Dock::dockRelativeTo(ui::Widget* relative,
|
|||
|
||||
Dock* subdock = new Dock;
|
||||
subdock->m_autoDelete = true;
|
||||
subdock->m_customizing = m_customizing;
|
||||
parent->replaceChild(relative, subdock);
|
||||
subdock->dock(CENTER, relative);
|
||||
subdock->dock(side, widget, prefSize);
|
||||
|
@ -241,6 +258,7 @@ Dock* Dock::subdock(int side)
|
|||
auto oldWidget = m_sides[i];
|
||||
auto newSubdock = new Dock;
|
||||
newSubdock->m_autoDelete = true;
|
||||
newSubdock->m_customizing = m_customizing;
|
||||
setSide(i, newSubdock);
|
||||
|
||||
if (oldWidget) {
|
||||
|
@ -280,16 +298,72 @@ void Dock::onResize(ui::ResizeEvent& ev)
|
|||
updateDockVisibility();
|
||||
|
||||
forEachSide(bounds,
|
||||
[bounds](ui::Widget* widget,
|
||||
[this](ui::Widget* widget,
|
||||
const gfx::Rect& widgetBounds,
|
||||
const gfx::Rect& separator,
|
||||
const int index) { widget->setBounds(widgetBounds); });
|
||||
const int index) {
|
||||
auto rc = widgetBounds;
|
||||
auto th = textHeight();
|
||||
if (isCustomizing()) {
|
||||
int handleSide = 0;
|
||||
if (auto dockable = dynamic_cast<Dockable*>(widget))
|
||||
handleSide = dockable->dockHandleSide();
|
||||
switch (handleSide) {
|
||||
case ui::TOP:
|
||||
rc.y += th;
|
||||
rc.h -= th;
|
||||
break;
|
||||
case ui::LEFT:
|
||||
rc.x += th;
|
||||
rc.w -= th;
|
||||
break;
|
||||
}
|
||||
}
|
||||
widget->setBounds(rc);
|
||||
});
|
||||
}
|
||||
|
||||
void Dock::onPaint(ui::PaintEvent& ev)
|
||||
{
|
||||
Graphics* g = ev.graphics();
|
||||
g->fillRect(bgColor(), clientBounds());
|
||||
gfx::Rect bounds = clientBounds();
|
||||
g->fillRect(bgColor(), bounds);
|
||||
|
||||
if (isCustomizing()) {
|
||||
forEachSide(bounds,
|
||||
[this, g](ui::Widget* widget,
|
||||
const gfx::Rect& widgetBounds,
|
||||
const gfx::Rect& separator,
|
||||
const int index) {
|
||||
auto rc = widgetBounds;
|
||||
auto th = textHeight();
|
||||
if (isCustomizing()) {
|
||||
auto theme = SkinTheme::get(this);
|
||||
auto color = theme->colors.workspaceText();
|
||||
int handleSide = 0;
|
||||
if (auto dockable = dynamic_cast<Dockable*>(widget))
|
||||
handleSide = dockable->dockHandleSide();
|
||||
switch (handleSide) {
|
||||
case ui::TOP:
|
||||
rc.h = th;
|
||||
for (int y = rc.y; y + 1 < rc.y2(); y += 2)
|
||||
g->drawHLine(color,
|
||||
rc.x + widget->border().left(),
|
||||
y,
|
||||
rc.w - widget->border().width());
|
||||
break;
|
||||
case ui::LEFT:
|
||||
rc.w = th;
|
||||
for (int x = rc.x; x + 1 < rc.x2(); x += 2)
|
||||
g->drawVLine(color,
|
||||
x,
|
||||
rc.y + widget->border().top(),
|
||||
rc.h - widget->border().height());
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Dock::onInitTheme(ui::InitThemeEvent& ev)
|
||||
|
@ -305,22 +379,18 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
|||
case kMouseDownMessage: {
|
||||
const gfx::Point pos = static_cast<MouseMessage*>(msg)->position();
|
||||
|
||||
m_capturedSide = -1;
|
||||
forEachSide(childrenBounds(),
|
||||
[this, pos](ui::Widget* widget,
|
||||
const gfx::Rect& widgetBounds,
|
||||
const gfx::Rect& separator,
|
||||
const int index) {
|
||||
if (separator.contains(pos)) {
|
||||
m_capturedWidget = widget;
|
||||
m_capturedSide = index;
|
||||
m_startSize = m_sizes[index];
|
||||
if (m_hit.sideIndex >= 0 || m_hit.dockable) {
|
||||
m_startPos = pos;
|
||||
}
|
||||
});
|
||||
|
||||
if (m_capturedSide >= 0) {
|
||||
if (m_hit.sideIndex >= 0) {
|
||||
m_startSize = m_sizes[m_hit.sideIndex];
|
||||
}
|
||||
|
||||
captureMouse();
|
||||
|
||||
if (m_hit.dockable)
|
||||
invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
@ -328,11 +398,11 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
|||
|
||||
case kMouseMoveMessage: {
|
||||
if (hasCapture()) {
|
||||
if (m_capturedSide >= 0) {
|
||||
if (m_hit.sideIndex >= 0) {
|
||||
const gfx::Point pos = static_cast<MouseMessage*>(msg)->position();
|
||||
gfx::Size& sz = m_sizes[m_capturedSide];
|
||||
gfx::Size& sz = m_sizes[m_hit.sideIndex];
|
||||
|
||||
switch (m_capturedSide) {
|
||||
switch (m_hit.sideIndex) {
|
||||
case kTopIndex: sz.h = (m_startSize.h + pos.y - m_startPos.y); break;
|
||||
case kBottomIndex: sz.h = (m_startSize.h - pos.y + m_startPos.y); break;
|
||||
case kLeftIndex: sz.w = (m_startSize.w + pos.x - m_startPos.x); break;
|
||||
|
@ -342,6 +412,9 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
|||
layout();
|
||||
Resize();
|
||||
}
|
||||
else if (m_hit.dockable) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -357,12 +430,34 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
|||
case kSetCursorMessage: {
|
||||
const gfx::Point pos = static_cast<MouseMessage*>(msg)->position();
|
||||
ui::CursorType cursor = ui::kArrowCursor;
|
||||
forEachSide(childrenBounds(),
|
||||
[pos, &cursor](ui::Widget* widget,
|
||||
|
||||
if (!hasCapture())
|
||||
m_hit = calcHit(pos);
|
||||
|
||||
if (m_hit.sideIndex >= 0) {
|
||||
switch (m_hit.sideIndex) {
|
||||
case kTopIndex:
|
||||
case kBottomIndex: cursor = ui::kSizeNSCursor; break;
|
||||
case kLeftIndex:
|
||||
case kRightIndex: cursor = ui::kSizeWECursor; break;
|
||||
}
|
||||
}
|
||||
else if (m_hit.dockable) {
|
||||
cursor = ui::kMoveCursor;
|
||||
}
|
||||
|
||||
#if 0
|
||||
m_hit = Hit();
|
||||
forEachSide(
|
||||
childrenBounds(),
|
||||
[this, pos, &cursor](ui::Widget* widget,
|
||||
const gfx::Rect& widgetBounds,
|
||||
const gfx::Rect& separator,
|
||||
const int index) {
|
||||
if (separator.contains(pos)) {
|
||||
m_hit.widget = widget;
|
||||
m_hit.sideIndex = index;
|
||||
|
||||
if (index == kTopIndex || index == kBottomIndex) {
|
||||
cursor = ui::kSizeNSCursor;
|
||||
}
|
||||
|
@ -370,7 +465,36 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
|||
cursor = ui::kSizeWECursor;
|
||||
}
|
||||
}
|
||||
else if (isCustomizing()) {
|
||||
auto th = textHeight();
|
||||
auto rc = widgetBounds;
|
||||
auto theme = SkinTheme::get(this);
|
||||
auto color = theme->colors.workspaceText();
|
||||
if (auto dockable = dynamic_cast<Dockable*>(widget)) {
|
||||
int handleSide = dockable->dockHandleSide();
|
||||
switch (handleSide) {
|
||||
case ui::TOP:
|
||||
rc.h = th;
|
||||
if (rc.contains(pos)) {
|
||||
cursor = ui::kMoveCursor;
|
||||
m_hit.widget = widget;
|
||||
m_hit.dockable = dockable;
|
||||
}
|
||||
break;
|
||||
case ui::LEFT:
|
||||
rc.w = th;
|
||||
if (rc.contains(pos)) {
|
||||
cursor = ui::kMoveCursor;
|
||||
m_hit.widget = widget;
|
||||
m_hit.dockable = dockable;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
ui::set_mouse_cursor(cursor);
|
||||
return true;
|
||||
}
|
||||
|
@ -526,4 +650,44 @@ void Dock::forEachSide(gfx::Rect bounds,
|
|||
}
|
||||
}
|
||||
|
||||
Dock::Hit Dock::calcHit(const gfx::Point& pos)
|
||||
{
|
||||
Hit hit;
|
||||
forEachSide(childrenBounds(),
|
||||
[this, pos, &hit](ui::Widget* widget,
|
||||
const gfx::Rect& widgetBounds,
|
||||
const gfx::Rect& separator,
|
||||
const int index) {
|
||||
if (separator.contains(pos)) {
|
||||
hit.widget = widget;
|
||||
hit.sideIndex = index;
|
||||
}
|
||||
else if (isCustomizing()) {
|
||||
auto th = textHeight();
|
||||
auto rc = widgetBounds;
|
||||
auto theme = SkinTheme::get(this);
|
||||
if (auto dockable = dynamic_cast<Dockable*>(widget)) {
|
||||
int handleSide = dockable->dockHandleSide();
|
||||
switch (handleSide) {
|
||||
case ui::TOP:
|
||||
rc.h = th;
|
||||
if (rc.contains(pos)) {
|
||||
hit.widget = widget;
|
||||
hit.dockable = dockable;
|
||||
}
|
||||
break;
|
||||
case ui::LEFT:
|
||||
rc.w = th;
|
||||
if (rc.contains(pos)) {
|
||||
hit.widget = widget;
|
||||
hit.dockable = dockable;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return hit;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
namespace app {
|
||||
|
||||
class Dockable;
|
||||
|
||||
class DockTabs : public ui::Widget {
|
||||
public:
|
||||
protected:
|
||||
|
@ -33,6 +35,9 @@ public:
|
|||
|
||||
Dock();
|
||||
|
||||
bool isCustomizing() const { return m_customizing; }
|
||||
void setCustomizing(bool enable, bool doLayout = true);
|
||||
|
||||
void resetDocks();
|
||||
|
||||
// side = ui::LEFT, or ui::RIGHT, etc.
|
||||
|
@ -80,16 +85,28 @@ private:
|
|||
|
||||
bool hasVisibleSide(const int i) const { return (m_sides[i] && m_sides[i]->isVisible()); }
|
||||
|
||||
struct Hit {
|
||||
ui::Widget* widget = nullptr;
|
||||
Dockable* dockable = nullptr;
|
||||
int sideIndex = -1;
|
||||
};
|
||||
|
||||
Hit calcHit(const gfx::Point& pos);
|
||||
|
||||
std::array<Widget*, kSides> m_sides;
|
||||
std::array<int, kSides> m_aligns;
|
||||
std::array<gfx::Size, kSides> m_sizes;
|
||||
bool m_autoDelete = false;
|
||||
|
||||
// Used to drag-and-drop sides.
|
||||
ui::Widget* m_capturedWidget = nullptr;
|
||||
int m_capturedSide;
|
||||
// Use to drag-and-drop stuff (splitters and dockable widgets)
|
||||
Hit m_hit;
|
||||
|
||||
// Used to resize sizes splitters.
|
||||
gfx::Size m_startSize;
|
||||
gfx::Point m_startPos;
|
||||
|
||||
// True when we paint/can drag-and-drop dockable widgets from handles.
|
||||
bool m_customizing = false;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -26,6 +26,10 @@ public:
|
|||
{
|
||||
return ui::LEFT | ui::TOP | ui::RIGHT | ui::BOTTOM | ui::CENTER | ui::EXPANSIVE;
|
||||
}
|
||||
|
||||
// Returns the preferred side where the dock handle to move the
|
||||
// widget should be.
|
||||
virtual int dockHandleSide() const { return ui::TOP; }
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -382,6 +382,9 @@ void LayoutSelector::switchSelector()
|
|||
|
||||
m_comboBox.setSizeHint(m_startSize);
|
||||
startAnimation((expand ? ANI_EXPANDING : ANI_COLLAPSING), ANI_TICKS);
|
||||
|
||||
MainWindow* win = App::instance()->mainWindow();
|
||||
win->setCustomizeDock(expand);
|
||||
}
|
||||
|
||||
void LayoutSelector::switchSelectorFromCommand()
|
||||
|
|
|
@ -450,6 +450,11 @@ void MainWindow::loadUserLayout(const Layout* layout)
|
|||
this->layout();
|
||||
}
|
||||
|
||||
void MainWindow::setCustomizeDock(bool enable)
|
||||
{
|
||||
m_customizableDock->setCustomizing(enable);
|
||||
}
|
||||
|
||||
void MainWindow::dataRecoverySessionsAreReady()
|
||||
{
|
||||
getHomeView()->dataRecoverySessionsAreReady();
|
||||
|
|
|
@ -96,6 +96,7 @@ public:
|
|||
void setMirroredDefaultLayout();
|
||||
void loadUserLayout(const Layout* layout);
|
||||
const Dock* customizableDock() const { return m_customizableDock; }
|
||||
void setCustomizeDock(bool enable);
|
||||
|
||||
// When crash::DataRecovery finish to search for sessions, this
|
||||
// function is called.
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
|
||||
// Dockable impl
|
||||
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||
int dockHandleSide() const override { return ui::LEFT; }
|
||||
|
||||
protected:
|
||||
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace app {
|
|||
class WorkspaceTabs;
|
||||
|
||||
class Workspace : public ui::Widget,
|
||||
public app::InputChainElement {
|
||||
public app::InputChainElement,
|
||||
public Dockable {
|
||||
public:
|
||||
typedef WorkspaceViews::iterator iterator;
|
||||
|
||||
|
@ -75,6 +76,11 @@ public:
|
|||
|
||||
WorkspacePanel* mainPanel() { return &m_mainPanel; }
|
||||
|
||||
// Dockable impl
|
||||
int dockableAt() const override { return 0; }
|
||||
int dockHandleSide() const override { return 0; } // No handles
|
||||
|
||||
// Signals
|
||||
obs::signal<void()> BeforeViewChanged;
|
||||
obs::signal<void()> ActiveViewChanged;
|
||||
|
||||
|
|
Loading…
Reference in New Issue