mirror of https://github.com/aseprite/aseprite.git
Add Dock widget, initial & basic version of dockable elements (#518)
Some missing features so far: 1) Restore old layout configuration (color bar split pos, timeline pos, etc.) and migrate to new Dock layout 2) Load/saving Dock layout 3) Create & customize current layoout (drag-and-drop widgets, etc.)
This commit is contained in:
parent
cef92c1a38
commit
fa21d87ba8
|
@ -1,30 +0,0 @@
|
||||||
<!-- Aseprite -->
|
|
||||||
<!-- Copyright (C) 2001-2017 by David Capello -->
|
|
||||||
<gui>
|
|
||||||
<window id="main_window" noborders="true" desktop="true">
|
|
||||||
<vbox noborders="true" expansive="true">
|
|
||||||
<hbox noborders="true" id="menu_bar_placeholder" />
|
|
||||||
<hbox noborders="true" id="tabs_placeholder" />
|
|
||||||
<splitter id="color_bar_splitter"
|
|
||||||
horizontal="true" expansive="true"
|
|
||||||
by="pixel"
|
|
||||||
style="workspace_splitter">
|
|
||||||
<vbox noborders="true" id="color_bar_placeholder" />
|
|
||||||
<vbox noborders="true" expansive="true">
|
|
||||||
<vbox noborders="true" id="context_bar_placeholder" />
|
|
||||||
<hbox noborders="true" expansive="true">
|
|
||||||
<splitter id="timeline_splitter"
|
|
||||||
vertical="true" expansive="true"
|
|
||||||
by="percetage" position="100"
|
|
||||||
style="workspace_splitter">
|
|
||||||
<hbox noborders="true" id="workspace_placeholder" expansive="true" />
|
|
||||||
<vbox noborders="true" id="timeline_placeholder" expansive="true" />
|
|
||||||
</splitter>
|
|
||||||
<vbox noborders="true" id="tool_bar_placeholder" />
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
</splitter>
|
|
||||||
<hbox noborders="true" id="status_bar_placeholder" />
|
|
||||||
</vbox>
|
|
||||||
</window>
|
|
||||||
</gui>
|
|
|
@ -609,6 +609,7 @@ target_sources(app-lib PRIVATE
|
||||||
ui/context_bar.cpp
|
ui/context_bar.cpp
|
||||||
ui/dithering_selector.cpp
|
ui/dithering_selector.cpp
|
||||||
ui/doc_view.cpp
|
ui/doc_view.cpp
|
||||||
|
ui/dock.cpp
|
||||||
ui/drop_down_button.cpp
|
ui/drop_down_button.cpp
|
||||||
ui/dynamics_popup.cpp
|
ui/dynamics_popup.cpp
|
||||||
ui/editor/brush_preview.cpp
|
ui/editor/brush_preview.cpp
|
||||||
|
@ -653,6 +654,7 @@ target_sources(app-lib PRIVATE
|
||||||
ui/input_chain.cpp
|
ui/input_chain.cpp
|
||||||
ui/keyboard_shortcuts.cpp
|
ui/keyboard_shortcuts.cpp
|
||||||
ui/layer_frame_comboboxes.cpp
|
ui/layer_frame_comboboxes.cpp
|
||||||
|
ui/layout_selector.cpp
|
||||||
ui/main_menu_bar.cpp
|
ui/main_menu_bar.cpp
|
||||||
ui/main_window.cpp
|
ui/main_window.cpp
|
||||||
ui/mini_help_button.cpp
|
ui/mini_help_button.cpp
|
||||||
|
|
|
@ -138,8 +138,8 @@ void ColorBar::ScrollableView::onInitTheme(InitThemeEvent& ev)
|
||||||
|
|
||||||
ColorBar* ColorBar::m_instance = NULL;
|
ColorBar* ColorBar::m_instance = NULL;
|
||||||
|
|
||||||
ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
ColorBar::ColorBar(TooltipManager* tooltipManager)
|
||||||
: Box(align)
|
: Box(VERTICAL)
|
||||||
, m_editPal(1)
|
, m_editPal(1)
|
||||||
, m_buttons(int(PalButton::MAX))
|
, m_buttons(int(PalButton::MAX))
|
||||||
, m_tilesButton(1)
|
, m_tilesButton(1)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "app/tileset_mode.h"
|
#include "app/tileset_mode.h"
|
||||||
#include "app/ui/button_set.h"
|
#include "app/ui/button_set.h"
|
||||||
#include "app/ui/color_button.h"
|
#include "app/ui/color_button.h"
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "app/ui/input_chain_element.h"
|
#include "app/ui/input_chain_element.h"
|
||||||
#include "app/ui/palette_view.h"
|
#include "app/ui/palette_view.h"
|
||||||
#include "app/ui/tile_button.h"
|
#include "app/ui/tile_button.h"
|
||||||
|
@ -50,7 +51,8 @@ class ColorBar : public ui::Box,
|
||||||
public PaletteViewDelegate,
|
public PaletteViewDelegate,
|
||||||
public ContextObserver,
|
public ContextObserver,
|
||||||
public DocObserver,
|
public DocObserver,
|
||||||
public InputChainElement {
|
public InputChainElement,
|
||||||
|
public Dockable {
|
||||||
static ColorBar* m_instance;
|
static ColorBar* m_instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -65,7 +67,7 @@ public:
|
||||||
|
|
||||||
static ColorBar* instance() { return m_instance; }
|
static ColorBar* instance() { return m_instance; }
|
||||||
|
|
||||||
ColorBar(int align, ui::TooltipManager* tooltipManager);
|
ColorBar(ui::TooltipManager* tooltipManager);
|
||||||
~ColorBar();
|
~ColorBar();
|
||||||
|
|
||||||
void setPixelFormat(doc::PixelFormat pixelFormat);
|
void setPixelFormat(doc::PixelFormat pixelFormat);
|
||||||
|
@ -123,6 +125,13 @@ public:
|
||||||
bool onClear(Context* ctx) override;
|
bool onClear(Context* ctx) override;
|
||||||
void onCancel(Context* ctx) override;
|
void onCancel(Context* ctx) override;
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override
|
||||||
|
{
|
||||||
|
// TODO split the ColorBar in different dockable widgets
|
||||||
|
return ui::LEFT | ui::RIGHT | ui::EXPANSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
obs::signal<void()> ChangeSelection;
|
obs::signal<void()> ChangeSelection;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "app/tools/tool_loop_modifiers.h"
|
#include "app/tools/tool_loop_modifiers.h"
|
||||||
#include "app/ui/context_bar_observer.h"
|
#include "app/ui/context_bar_observer.h"
|
||||||
#include "app/ui/doc_observer_widget.h"
|
#include "app/ui/doc_observer_widget.h"
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "app/ui/font_entry.h"
|
#include "app/ui/font_entry.h"
|
||||||
#include "doc/brush.h"
|
#include "doc/brush.h"
|
||||||
#include "obs/connection.h"
|
#include "obs/connection.h"
|
||||||
|
@ -60,7 +61,8 @@ class Transformation;
|
||||||
|
|
||||||
class ContextBar : public DocObserverWidget<ui::HBox>,
|
class ContextBar : public DocObserverWidget<ui::HBox>,
|
||||||
public obs::observable<ContextBarObserver>,
|
public obs::observable<ContextBarObserver>,
|
||||||
public tools::ActiveToolObserver {
|
public tools::ActiveToolObserver,
|
||||||
|
public Dockable {
|
||||||
public:
|
public:
|
||||||
ContextBar(ui::TooltipManager* tooltipManager, ColorBar* colorBar);
|
ContextBar(ui::TooltipManager* tooltipManager, ColorBar* colorBar);
|
||||||
~ContextBar();
|
~ContextBar();
|
||||||
|
@ -100,6 +102,9 @@ public:
|
||||||
// For freehand with dynamics
|
// For freehand with dynamics
|
||||||
const tools::DynamicsOptions& getDynamics() const;
|
const tools::DynamicsOptions& getDynamics() const;
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
obs::signal<void()> BrushChange;
|
obs::signal<void()> BrushChange;
|
||||||
obs::signal<void(const FontInfo&, FontEntry::From)> FontChange;
|
obs::signal<void(const FontInfo&, FontEntry::From)> FontChange;
|
||||||
|
|
|
@ -0,0 +1,476 @@
|
||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2021 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "app/ui/dock.h"
|
||||||
|
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
|
#include "app/ui/skin/skin_theme.h"
|
||||||
|
#include "ui/cursor_type.h"
|
||||||
|
#include "ui/message.h"
|
||||||
|
#include "ui/paint_event.h"
|
||||||
|
#include "ui/resize_event.h"
|
||||||
|
#include "ui/scale.h"
|
||||||
|
#include "ui/size_hint_event.h"
|
||||||
|
#include "ui/system.h"
|
||||||
|
#include "ui/widget.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
using namespace app::skin;
|
||||||
|
using namespace ui;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum { kTopIndex, kBottomIndex, kLeftIndex, kRightIndex, kCenterIndex };
|
||||||
|
|
||||||
|
int side_index(int side)
|
||||||
|
{
|
||||||
|
switch (side) {
|
||||||
|
case ui::TOP: return kTopIndex;
|
||||||
|
case ui::BOTTOM: return kBottomIndex;
|
||||||
|
case ui::LEFT: return kLeftIndex;
|
||||||
|
case ui::RIGHT: return kRightIndex;
|
||||||
|
}
|
||||||
|
return kCenterIndex; // ui::CENTER
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void DockTabs::onSizeHint(ui::SizeHintEvent& ev)
|
||||||
|
{
|
||||||
|
gfx::Size sz;
|
||||||
|
for (auto child : children()) {
|
||||||
|
if (child->isVisible())
|
||||||
|
sz |= child->sizeHint();
|
||||||
|
}
|
||||||
|
sz.h += textHeight();
|
||||||
|
ev.setSizeHint(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockTabs::onResize(ui::ResizeEvent& ev)
|
||||||
|
{
|
||||||
|
auto bounds = ev.bounds();
|
||||||
|
setBoundsQuietly(bounds);
|
||||||
|
bounds = childrenBounds();
|
||||||
|
bounds.y += textHeight();
|
||||||
|
bounds.h -= textHeight();
|
||||||
|
|
||||||
|
for (auto child : children()) {
|
||||||
|
child->setBounds(bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockTabs::onPaint(ui::PaintEvent& ev)
|
||||||
|
{
|
||||||
|
Graphics* g = ev.graphics();
|
||||||
|
g->fillRect(gfx::rgba(0, 0, 255), clientBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
Dock::Dock()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kSides; ++i) {
|
||||||
|
m_sides[i] = nullptr;
|
||||||
|
m_aligns[i] = 0;
|
||||||
|
m_sizes[i] = gfx::Size(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
InitTheme.connect([this] {
|
||||||
|
if (auto p = parent())
|
||||||
|
setBgColor(p->bgColor());
|
||||||
|
});
|
||||||
|
initTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::reset()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kSides; ++i) {
|
||||||
|
auto child = m_sides[i];
|
||||||
|
if (!child)
|
||||||
|
continue;
|
||||||
|
else if (auto subdock = dynamic_cast<Dock*>(child)) {
|
||||||
|
subdock->reset();
|
||||||
|
}
|
||||||
|
else if (auto tabs = dynamic_cast<DockTabs*>(child)) {
|
||||||
|
for (auto child2 : tabs->children()) {
|
||||||
|
if (auto subdock2 = dynamic_cast<Dock*>(child2))
|
||||||
|
subdock2->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_sides[i] = nullptr;
|
||||||
|
}
|
||||||
|
removeAllChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::dock(int side, ui::Widget* widget, const gfx::Size& prefSize)
|
||||||
|
{
|
||||||
|
ASSERT(widget);
|
||||||
|
|
||||||
|
const int i = side_index(side);
|
||||||
|
if (!m_sides[i]) {
|
||||||
|
setSide(i, widget);
|
||||||
|
addChild(widget);
|
||||||
|
|
||||||
|
if (prefSize != gfx::Size(0, 0))
|
||||||
|
m_sizes[i] = prefSize;
|
||||||
|
}
|
||||||
|
else if (auto subdock = dynamic_cast<Dock*>(m_sides[i])) {
|
||||||
|
subdock->dock(CENTER, widget, prefSize);
|
||||||
|
}
|
||||||
|
else if (auto tabs = dynamic_cast<DockTabs*>(m_sides[i])) {
|
||||||
|
tabs->addChild(widget);
|
||||||
|
}
|
||||||
|
// If this side already contains a widget, we create a DockTabs in
|
||||||
|
// this side.
|
||||||
|
else {
|
||||||
|
auto oldWidget = m_sides[i];
|
||||||
|
auto newTabs = new DockTabs;
|
||||||
|
replaceChild(oldWidget, newTabs);
|
||||||
|
newTabs->addChild(oldWidget);
|
||||||
|
newTabs->addChild(widget);
|
||||||
|
setSide(i, newTabs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::dockRelativeTo(ui::Widget* relative,
|
||||||
|
int side,
|
||||||
|
ui::Widget* widget,
|
||||||
|
const gfx::Size& prefSize)
|
||||||
|
{
|
||||||
|
ASSERT(relative);
|
||||||
|
|
||||||
|
Widget* parent = relative->parent();
|
||||||
|
ASSERT(parent);
|
||||||
|
|
||||||
|
Dock* subdock = new Dock;
|
||||||
|
parent->replaceChild(relative, subdock);
|
||||||
|
subdock->dock(CENTER, relative);
|
||||||
|
subdock->dock(side, widget, prefSize);
|
||||||
|
|
||||||
|
// Fix the m_sides item if the parent is a Dock
|
||||||
|
if (auto relativeDock = dynamic_cast<Dock*>(parent)) {
|
||||||
|
for (int i = 0; i < kSides; ++i) {
|
||||||
|
if (relativeDock->m_sides[i] == relative) {
|
||||||
|
relativeDock->setSide(i, subdock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::undock(Widget* widget)
|
||||||
|
{
|
||||||
|
Widget* parent = widget->parent();
|
||||||
|
if (!parent)
|
||||||
|
return; // Already undocked
|
||||||
|
|
||||||
|
if (auto parentDock = dynamic_cast<Dock*>(parent)) {
|
||||||
|
parentDock->removeChild(widget);
|
||||||
|
|
||||||
|
for (int i = 0; i < kSides; ++i) {
|
||||||
|
if (parentDock->m_sides[i] == widget) {
|
||||||
|
parentDock->setSide(i, nullptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentDock != this && parentDock->children().empty()) {
|
||||||
|
undock(parentDock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto parentTabs = dynamic_cast<DockTabs*>(parent)) {
|
||||||
|
parentTabs->removeChild(widget);
|
||||||
|
|
||||||
|
if (parentTabs->children().empty()) {
|
||||||
|
undock(parentTabs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parent->removeChild(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dock* Dock::subdock(int side)
|
||||||
|
{
|
||||||
|
int i = side_index(side);
|
||||||
|
if (auto subdock = dynamic_cast<Dock*>(m_sides[i]))
|
||||||
|
return subdock;
|
||||||
|
|
||||||
|
auto oldWidget = m_sides[i];
|
||||||
|
auto newSubdock = new Dock;
|
||||||
|
setSide(i, newSubdock);
|
||||||
|
|
||||||
|
if (oldWidget) {
|
||||||
|
replaceChild(oldWidget, newSubdock);
|
||||||
|
newSubdock->dock(CENTER, oldWidget);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
addChild(newSubdock);
|
||||||
|
|
||||||
|
return newSubdock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::onSizeHint(ui::SizeHintEvent& ev)
|
||||||
|
{
|
||||||
|
gfx::Size sz = border().size();
|
||||||
|
|
||||||
|
if (m_sides[kLeftIndex])
|
||||||
|
sz.w += m_sides[kLeftIndex]->sizeHint().w + childSpacing();
|
||||||
|
if (m_sides[kRightIndex])
|
||||||
|
sz.w += m_sides[kRightIndex]->sizeHint().w + childSpacing();
|
||||||
|
if (m_sides[kTopIndex])
|
||||||
|
sz.h += m_sides[kTopIndex]->sizeHint().h + childSpacing();
|
||||||
|
if (m_sides[kBottomIndex])
|
||||||
|
sz.h += m_sides[kBottomIndex]->sizeHint().h + childSpacing();
|
||||||
|
if (m_sides[kCenterIndex]) {
|
||||||
|
sz += m_sides[kCenterIndex]->sizeHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.setSizeHint(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::onResize(ui::ResizeEvent& ev)
|
||||||
|
{
|
||||||
|
auto bounds = ev.bounds();
|
||||||
|
setBoundsQuietly(bounds);
|
||||||
|
bounds = childrenBounds();
|
||||||
|
|
||||||
|
updateDockVisibility();
|
||||||
|
|
||||||
|
forEachSide(bounds,
|
||||||
|
[bounds](ui::Widget* widget,
|
||||||
|
const gfx::Rect& widgetBounds,
|
||||||
|
const gfx::Rect& separator,
|
||||||
|
const int index) { widget->setBounds(widgetBounds); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::onPaint(ui::PaintEvent& ev)
|
||||||
|
{
|
||||||
|
Graphics* g = ev.graphics();
|
||||||
|
g->fillRect(bgColor(), clientBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::onInitTheme(ui::InitThemeEvent& ev)
|
||||||
|
{
|
||||||
|
Widget::onInitTheme(ev);
|
||||||
|
setBorder(gfx::Border(0));
|
||||||
|
setChildSpacing(4 * ui::guiscale());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Dock::onProcessMessage(ui::Message* msg)
|
||||||
|
{
|
||||||
|
switch (msg->type()) {
|
||||||
|
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];
|
||||||
|
m_startPos = pos;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_capturedSide >= 0) {
|
||||||
|
captureMouse();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kMouseMoveMessage: {
|
||||||
|
if (hasCapture()) {
|
||||||
|
if (m_capturedSide >= 0) {
|
||||||
|
const gfx::Point pos = static_cast<MouseMessage*>(msg)->position();
|
||||||
|
gfx::Size& sz = m_sizes[m_capturedSide];
|
||||||
|
|
||||||
|
switch (m_capturedSide) {
|
||||||
|
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;
|
||||||
|
case kRightIndex: sz.w = (m_startSize.w - pos.x + m_startPos.x); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kMouseUpMessage: {
|
||||||
|
if (hasCapture()) {
|
||||||
|
releaseMouse();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kSetCursorMessage: {
|
||||||
|
const gfx::Point pos = static_cast<MouseMessage*>(msg)->position();
|
||||||
|
ui::CursorType cursor = ui::kArrowCursor;
|
||||||
|
forEachSide(childrenBounds(),
|
||||||
|
[pos, &cursor](ui::Widget* widget,
|
||||||
|
const gfx::Rect& widgetBounds,
|
||||||
|
const gfx::Rect& separator,
|
||||||
|
const int index) {
|
||||||
|
if (separator.contains(pos)) {
|
||||||
|
if (index == kTopIndex || index == kBottomIndex) {
|
||||||
|
cursor = ui::kSizeNSCursor;
|
||||||
|
}
|
||||||
|
else if (index == kLeftIndex || index == kRightIndex) {
|
||||||
|
cursor = ui::kSizeWECursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui::set_mouse_cursor(cursor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Widget::onProcessMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::setSide(const int i, Widget* newWidget)
|
||||||
|
{
|
||||||
|
m_sides[i] = newWidget;
|
||||||
|
m_aligns[i] = calcAlign(i);
|
||||||
|
|
||||||
|
if (newWidget) {
|
||||||
|
m_sizes[i] = newWidget->sizeHint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Dock::calcAlign(const int i)
|
||||||
|
{
|
||||||
|
Widget* widget = m_sides[i];
|
||||||
|
int align = 0;
|
||||||
|
if (!widget) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
else if (auto subdock = dynamic_cast<Dock*>(widget)) {
|
||||||
|
align = subdock->calcAlign(i);
|
||||||
|
}
|
||||||
|
else if (auto tabs = dynamic_cast<DockTabs*>(widget)) {
|
||||||
|
for (auto child : tabs->children()) {
|
||||||
|
if (auto subdock2 = dynamic_cast<Dock*>(widget))
|
||||||
|
align |= subdock2->calcAlign(i);
|
||||||
|
else if (auto dockable = dynamic_cast<Dockable*>(child)) {
|
||||||
|
align = dockable->dockableAt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto dockable2 = dynamic_cast<Dockable*>(widget)) {
|
||||||
|
align = dockable2->dockableAt();
|
||||||
|
}
|
||||||
|
return align;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::updateDockVisibility()
|
||||||
|
{
|
||||||
|
bool visible = false;
|
||||||
|
setVisible(true);
|
||||||
|
for (int i = 0; i < kSides; ++i) {
|
||||||
|
Widget* widget = m_sides[i];
|
||||||
|
if (!widget)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (auto subdock = dynamic_cast<Dock*>(widget)) {
|
||||||
|
subdock->updateDockVisibility();
|
||||||
|
}
|
||||||
|
else if (auto tabs = dynamic_cast<DockTabs*>(widget)) {
|
||||||
|
bool visible2 = false;
|
||||||
|
for (auto child : tabs->children()) {
|
||||||
|
if (auto subdock2 = dynamic_cast<Dock*>(widget)) {
|
||||||
|
subdock2->updateDockVisibility();
|
||||||
|
}
|
||||||
|
if (child->isVisible()) {
|
||||||
|
visible2 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tabs->setVisible(visible2);
|
||||||
|
if (visible2)
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget->isVisible()) {
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dock::forEachSide(gfx::Rect bounds,
|
||||||
|
std::function<void(ui::Widget* widget,
|
||||||
|
const gfx::Rect& widgetBounds,
|
||||||
|
const gfx::Rect& separator,
|
||||||
|
const int index)> f)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kSides; ++i) {
|
||||||
|
auto widget = m_sides[i];
|
||||||
|
if (!widget || !widget->isVisible()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spacing = (m_aligns[i] & EXPANSIVE ? childSpacing() : 0);
|
||||||
|
|
||||||
|
const gfx::Size sz = (m_aligns[i] & EXPANSIVE ? m_sizes[i] : widget->sizeHint());
|
||||||
|
gfx::Rect rc, separator;
|
||||||
|
switch (i) {
|
||||||
|
case kTopIndex:
|
||||||
|
rc = gfx::Rect(bounds.x, bounds.y, bounds.w, sz.h);
|
||||||
|
bounds.y += rc.h;
|
||||||
|
bounds.h -= rc.h;
|
||||||
|
|
||||||
|
if (spacing > 0) {
|
||||||
|
separator = gfx::Rect(bounds.x, bounds.y, bounds.w, spacing);
|
||||||
|
bounds.y += spacing;
|
||||||
|
bounds.h -= spacing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kBottomIndex:
|
||||||
|
rc = gfx::Rect(bounds.x, bounds.y2() - sz.h, bounds.w, sz.h);
|
||||||
|
bounds.h -= rc.h;
|
||||||
|
|
||||||
|
if (spacing > 0) {
|
||||||
|
separator = gfx::Rect(bounds.x, bounds.y2() - spacing, bounds.w, spacing);
|
||||||
|
bounds.h -= spacing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kLeftIndex:
|
||||||
|
rc = gfx::Rect(bounds.x, bounds.y, sz.w, bounds.h);
|
||||||
|
bounds.x += rc.w;
|
||||||
|
bounds.w -= rc.w;
|
||||||
|
|
||||||
|
if (spacing > 0) {
|
||||||
|
separator = gfx::Rect(bounds.x, bounds.y, spacing, bounds.h);
|
||||||
|
bounds.x += spacing;
|
||||||
|
bounds.w -= spacing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kRightIndex:
|
||||||
|
rc = gfx::Rect(bounds.x2() - sz.w, bounds.y, sz.w, bounds.h);
|
||||||
|
bounds.w -= rc.w;
|
||||||
|
|
||||||
|
if (spacing > 0) {
|
||||||
|
separator = gfx::Rect(bounds.x2() - spacing, bounds.y, spacing, bounds.h);
|
||||||
|
bounds.w -= spacing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kCenterIndex: rc = bounds; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
f(widget, rc, separator, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace app
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2021 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_UI_DOCK_H_INCLUDED
|
||||||
|
#define APP_UI_DOCK_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gfx/rect.h"
|
||||||
|
#include "gfx/size.h"
|
||||||
|
#include "ui/widget.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
class DockTabs : public ui::Widget {
|
||||||
|
public:
|
||||||
|
protected:
|
||||||
|
void onSizeHint(ui::SizeHintEvent& ev) override;
|
||||||
|
void onResize(ui::ResizeEvent& ev) override;
|
||||||
|
void onPaint(ui::PaintEvent& ev) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Dock : public ui::Widget {
|
||||||
|
public:
|
||||||
|
static constexpr const int kSides = 5;
|
||||||
|
|
||||||
|
Dock();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// side = ui::LEFT, or ui::RIGHT, etc.
|
||||||
|
void dock(int side, ui::Widget* widget, const gfx::Size& prefSize = gfx::Size());
|
||||||
|
|
||||||
|
void dockRelativeTo(ui::Widget* relative,
|
||||||
|
int side,
|
||||||
|
ui::Widget* widget,
|
||||||
|
const gfx::Size& prefSize = gfx::Size());
|
||||||
|
|
||||||
|
void undock(ui::Widget* widget);
|
||||||
|
|
||||||
|
Dock* subdock(int side);
|
||||||
|
|
||||||
|
Dock* top() { return subdock(ui::TOP); }
|
||||||
|
Dock* bottom() { return subdock(ui::BOTTOM); }
|
||||||
|
Dock* left() { return subdock(ui::LEFT); }
|
||||||
|
Dock* right() { return subdock(ui::RIGHT); }
|
||||||
|
Dock* center() { return subdock(ui::CENTER); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void onSizeHint(ui::SizeHintEvent& ev) override;
|
||||||
|
void onResize(ui::ResizeEvent& ev) override;
|
||||||
|
void onPaint(ui::PaintEvent& ev) override;
|
||||||
|
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||||
|
bool onProcessMessage(ui::Message* msg) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setSide(const int i, ui::Widget* newWidget);
|
||||||
|
int calcAlign(const int i);
|
||||||
|
void updateDockVisibility();
|
||||||
|
void forEachSide(gfx::Rect bounds,
|
||||||
|
std::function<void(ui::Widget* widget,
|
||||||
|
const gfx::Rect& widgetBounds,
|
||||||
|
const gfx::Rect& separator,
|
||||||
|
const int index)> f);
|
||||||
|
|
||||||
|
std::array<Widget*, kSides> m_sides;
|
||||||
|
std::array<int, kSides> m_aligns;
|
||||||
|
std::array<gfx::Size, kSides> m_sizes;
|
||||||
|
|
||||||
|
// Used to drag-and-drop sides.
|
||||||
|
ui::Widget* m_capturedWidget = nullptr;
|
||||||
|
int m_capturedSide;
|
||||||
|
gfx::Size m_startSize;
|
||||||
|
gfx::Point m_startPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2021 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_UI_DOCKABLE_H_INCLUDED
|
||||||
|
#define APP_UI_DOCKABLE_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/base.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
class Dockable {
|
||||||
|
public:
|
||||||
|
virtual ~Dockable() {}
|
||||||
|
|
||||||
|
// LEFT = can be docked at the left side
|
||||||
|
// TOP = can be docked at the top
|
||||||
|
// RIGHT = can be docked at the right side
|
||||||
|
// BOTTOM = can be docked at the bottom
|
||||||
|
// CENTER = can be docked at the center
|
||||||
|
// EXPANSIVE = can be resized (e.g. add a splitter when docked at sides)
|
||||||
|
virtual int dockableAt() const
|
||||||
|
{
|
||||||
|
return ui::LEFT | ui::TOP | ui::RIGHT | ui::BOTTOM | ui::CENTER | ui::EXPANSIVE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,134 @@
|
||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2021 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "app/ui/layout_selector.h"
|
||||||
|
|
||||||
|
#include "app/app.h"
|
||||||
|
#include "app/ui/main_window.h"
|
||||||
|
#include "app/ui/separator_in_view.h"
|
||||||
|
#include "app/ui/skin/skin_theme.h"
|
||||||
|
#include "ui/listitem.h"
|
||||||
|
#include "ui/window.h"
|
||||||
|
|
||||||
|
#define ANI_TICKS 5
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
using namespace app::skin;
|
||||||
|
using namespace ui;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum class LayoutId { DEFAULT, DEFAULT_MIRROR, CUSTOMIZE };
|
||||||
|
|
||||||
|
class LayoutItem : public ListItem {
|
||||||
|
public:
|
||||||
|
LayoutItem(const LayoutId id, const std::string& text) : ListItem(text), m_id(id) {}
|
||||||
|
|
||||||
|
void select()
|
||||||
|
{
|
||||||
|
MainWindow* win = App::instance()->mainWindow();
|
||||||
|
|
||||||
|
switch (m_id) {
|
||||||
|
case LayoutId::DEFAULT: win->setDefaultLayout(); break;
|
||||||
|
case LayoutId::DEFAULT_MIRROR: win->setDefaultMirrorLayout(); break;
|
||||||
|
case LayoutId::CUSTOMIZE:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LayoutId m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
void LayoutSelector::LayoutComboBox::onChange()
|
||||||
|
{
|
||||||
|
if (auto item = dynamic_cast<LayoutItem*>(getSelectedItem())) {
|
||||||
|
item->select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutSelector::LayoutSelector() : m_button("", "\xc3\xb7")
|
||||||
|
{
|
||||||
|
m_button.Click.connect([this]() { switchSelector(); });
|
||||||
|
|
||||||
|
m_comboBox.setVisible(false);
|
||||||
|
|
||||||
|
addChild(&m_comboBox);
|
||||||
|
addChild(&m_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutSelector::~LayoutSelector()
|
||||||
|
{
|
||||||
|
stopAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayoutSelector::onAnimationFrame()
|
||||||
|
{
|
||||||
|
switch (animation()) {
|
||||||
|
case ANI_NONE: break;
|
||||||
|
case ANI_EXPANDING:
|
||||||
|
case ANI_COLLAPSING: {
|
||||||
|
const double t = animationTime();
|
||||||
|
m_comboBox.setSizeHint(gfx::Size((1.0 - t) * m_startSize.w + t * m_endSize.w,
|
||||||
|
(1.0 - t) * m_startSize.h + t * m_endSize.h));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto win = window())
|
||||||
|
win->layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayoutSelector::onAnimationStop(int animation)
|
||||||
|
{
|
||||||
|
switch (animation) {
|
||||||
|
case ANI_EXPANDING: m_comboBox.setSizeHint(m_endSize); break;
|
||||||
|
case ANI_COLLAPSING:
|
||||||
|
m_comboBox.setVisible(false);
|
||||||
|
m_comboBox.setSizeHint(m_endSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto win = window())
|
||||||
|
win->layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayoutSelector::switchSelector()
|
||||||
|
{
|
||||||
|
bool expand;
|
||||||
|
if (!m_comboBox.isVisible()) {
|
||||||
|
expand = true;
|
||||||
|
|
||||||
|
// Create the combobox for first time
|
||||||
|
if (m_comboBox.getItemCount() == 0) {
|
||||||
|
m_comboBox.addItem(new LayoutItem(LayoutId::DEFAULT, "Default"));
|
||||||
|
m_comboBox.addItem(new LayoutItem(LayoutId::DEFAULT_MIRROR, "Default / Mirror"));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_comboBox.setVisible(true);
|
||||||
|
m_comboBox.resetSizeHint();
|
||||||
|
m_startSize = gfx::Size(0, 0);
|
||||||
|
m_endSize = m_comboBox.sizeHint();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expand = false;
|
||||||
|
m_startSize = m_comboBox.bounds().size();
|
||||||
|
m_endSize = gfx::Size(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_comboBox.setSizeHint(m_startSize);
|
||||||
|
startAnimation((expand ? ANI_EXPANDING : ANI_COLLAPSING), ANI_TICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace app
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2021-2022 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_UI_LAYOUT_SELECTOR_H_INCLUDED
|
||||||
|
#define APP_UI_LAYOUT_SELECTOR_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
|
#include "ui/animated_widget.h"
|
||||||
|
#include "ui/box.h"
|
||||||
|
#include "ui/combobox.h"
|
||||||
|
#include "ui/link_label.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
class LayoutSelector : public ui::HBox,
|
||||||
|
public ui::AnimatedWidget,
|
||||||
|
public Dockable {
|
||||||
|
enum Ani : int {
|
||||||
|
ANI_NONE,
|
||||||
|
ANI_EXPANDING,
|
||||||
|
ANI_COLLAPSING,
|
||||||
|
};
|
||||||
|
|
||||||
|
class LayoutComboBox : public ui::ComboBox {
|
||||||
|
void onChange() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
LayoutSelector();
|
||||||
|
~LayoutSelector();
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onAnimationFrame() override;
|
||||||
|
void onAnimationStop(int animation) override;
|
||||||
|
void switchSelector();
|
||||||
|
|
||||||
|
LayoutComboBox m_comboBox;
|
||||||
|
ui::LinkLabel m_button;
|
||||||
|
gfx::Size m_startSize;
|
||||||
|
gfx::Size m_endSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
|
@ -9,18 +9,23 @@
|
||||||
#define APP_UI_MAIN_MENU_BAR_H_INCLUDED
|
#define APP_UI_MAIN_MENU_BAR_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "obs/connection.h"
|
#include "obs/connection.h"
|
||||||
#include "ui/menu.h"
|
#include "ui/menu.h"
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
class MainMenuBar : public ui::MenuBar {
|
class MainMenuBar : public ui::MenuBar,
|
||||||
|
public Dockable {
|
||||||
public:
|
public:
|
||||||
MainMenuBar();
|
MainMenuBar();
|
||||||
|
|
||||||
void queueReload();
|
void queueReload();
|
||||||
void reload();
|
void reload();
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
obs::scoped_connection m_extKeys;
|
obs::scoped_connection m_extKeys;
|
||||||
obs::scoped_connection m_extScripts;
|
obs::scoped_connection m_extScripts;
|
||||||
|
|
|
@ -24,9 +24,11 @@
|
||||||
#include "app/ui/color_bar.h"
|
#include "app/ui/color_bar.h"
|
||||||
#include "app/ui/context_bar.h"
|
#include "app/ui/context_bar.h"
|
||||||
#include "app/ui/doc_view.h"
|
#include "app/ui/doc_view.h"
|
||||||
|
#include "app/ui/dock.h"
|
||||||
#include "app/ui/editor/editor.h"
|
#include "app/ui/editor/editor.h"
|
||||||
#include "app/ui/editor/editor_view.h"
|
#include "app/ui/editor/editor_view.h"
|
||||||
#include "app/ui/home_view.h"
|
#include "app/ui/home_view.h"
|
||||||
|
#include "app/ui/layout_selector.h"
|
||||||
#include "app/ui/main_menu_bar.h"
|
#include "app/ui/main_menu_bar.h"
|
||||||
#include "app/ui/notifications.h"
|
#include "app/ui/notifications.h"
|
||||||
#include "app/ui/preview_editor.h"
|
#include "app/ui/preview_editor.h"
|
||||||
|
@ -83,7 +85,10 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
MainWindow::MainWindow()
|
MainWindow::MainWindow()
|
||||||
: m_mode(NormalMode)
|
: ui::Window(ui::Window::DesktopWindow)
|
||||||
|
, m_dock(new Dock)
|
||||||
|
, m_customizableDock(new Dock)
|
||||||
|
, m_mode(NormalMode)
|
||||||
, m_homeView(nullptr)
|
, m_homeView(nullptr)
|
||||||
, m_scalePanic(nullptr)
|
, m_scalePanic(nullptr)
|
||||||
, m_browserView(nullptr)
|
, m_browserView(nullptr)
|
||||||
|
@ -105,8 +110,9 @@ MainWindow::MainWindow()
|
||||||
// Refer to https://github.com/aseprite/aseprite/issues/3914
|
// Refer to https://github.com/aseprite/aseprite/issues/3914
|
||||||
void MainWindow::initialize()
|
void MainWindow::initialize()
|
||||||
{
|
{
|
||||||
m_tooltipManager = new TooltipManager();
|
m_tooltipManager = new TooltipManager;
|
||||||
m_menuBar = new MainMenuBar();
|
m_menuBar = new MainMenuBar;
|
||||||
|
m_layoutSelector = new LayoutSelector;
|
||||||
|
|
||||||
// Register commands to load menus+shortcuts for these commands
|
// Register commands to load menus+shortcuts for these commands
|
||||||
Editor::registerCommands();
|
Editor::registerCommands();
|
||||||
|
@ -123,7 +129,7 @@ void MainWindow::initialize()
|
||||||
m_tabsBar = new WorkspaceTabs(this);
|
m_tabsBar = new WorkspaceTabs(this);
|
||||||
m_workspace = new Workspace();
|
m_workspace = new Workspace();
|
||||||
m_previewEditor = new PreviewEditorWindow();
|
m_previewEditor = new PreviewEditorWindow();
|
||||||
m_colorBar = new ColorBar(colorBarPlaceholder()->align(), m_tooltipManager);
|
m_colorBar = new ColorBar(m_tooltipManager);
|
||||||
m_contextBar = new ContextBar(m_tooltipManager, m_colorBar);
|
m_contextBar = new ContextBar(m_tooltipManager, m_colorBar);
|
||||||
|
|
||||||
// The timeline (AniControls) tooltips will use the keyboard
|
// The timeline (AniControls) tooltips will use the keyboard
|
||||||
|
@ -148,19 +154,24 @@ void MainWindow::initialize()
|
||||||
|
|
||||||
// Add the widgets in the boxes
|
// Add the widgets in the boxes
|
||||||
addChild(m_tooltipManager);
|
addChild(m_tooltipManager);
|
||||||
menuBarPlaceholder()->addChild(m_menuBar);
|
addChild(m_dock);
|
||||||
menuBarPlaceholder()->addChild(m_notifications);
|
|
||||||
contextBarPlaceholder()->addChild(m_contextBar);
|
|
||||||
colorBarPlaceholder()->addChild(m_colorBar);
|
|
||||||
toolBarPlaceholder()->addChild(m_toolBar);
|
|
||||||
statusBarPlaceholder()->addChild(m_statusBar);
|
|
||||||
tabsPlaceholder()->addChild(m_tabsBar);
|
|
||||||
workspacePlaceholder()->addChild(m_workspace);
|
|
||||||
timelinePlaceholder()->addChild(m_timeline);
|
|
||||||
|
|
||||||
// Default splitter positions
|
auto customizableDockPlaceholder = new Widget;
|
||||||
colorBarSplitter()->setPosition(m_colorBar->sizeHint().w);
|
customizableDockPlaceholder->addChild(m_customizableDock);
|
||||||
timelineSplitter()->setPosition(75);
|
customizableDockPlaceholder->InitTheme.connect([this] {
|
||||||
|
auto theme = static_cast<skin::SkinTheme*>(this->theme());
|
||||||
|
m_customizableDock->setBgColor(theme->colors.workspace());
|
||||||
|
});
|
||||||
|
customizableDockPlaceholder->initTheme();
|
||||||
|
|
||||||
|
m_dock->top()->right()->dock(ui::RIGHT, m_notifications);
|
||||||
|
m_dock->top()->right()->dock(ui::CENTER, m_layoutSelector);
|
||||||
|
m_dock->top()->dock(ui::BOTTOM, m_tabsBar);
|
||||||
|
m_dock->top()->dock(ui::CENTER, m_menuBar);
|
||||||
|
m_dock->dock(ui::CENTER, customizableDockPlaceholder);
|
||||||
|
m_dock->dock(ui::BOTTOM, m_statusBar);
|
||||||
|
|
||||||
|
setDefaultLayout();
|
||||||
|
|
||||||
// Reconfigure workspace when the timeline position is changed.
|
// Reconfigure workspace when the timeline position is changed.
|
||||||
auto& pref = Preferences::instance();
|
auto& pref = Preferences::instance();
|
||||||
|
@ -179,6 +190,9 @@ void MainWindow::initialize()
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
m_dock->reset();
|
||||||
|
m_customizableDock->reset();
|
||||||
|
|
||||||
delete m_scalePanic;
|
delete m_scalePanic;
|
||||||
|
|
||||||
#ifdef ENABLE_SCRIPTING
|
#ifdef ENABLE_SCRIPTING
|
||||||
|
@ -254,7 +268,7 @@ void MainWindow::showNotification(INotificationDelegate* del)
|
||||||
{
|
{
|
||||||
m_notifications->addLink(del);
|
m_notifications->addLink(del);
|
||||||
m_notifications->setVisible(true);
|
m_notifications->setVisible(true);
|
||||||
m_notifications->parent()->layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::showHomeOnOpen()
|
void MainWindow::showHomeOnOpen()
|
||||||
|
@ -360,6 +374,34 @@ void MainWindow::popTimeline()
|
||||||
setTimelineVisibility(true);
|
setTimelineVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setDefaultLayout()
|
||||||
|
{
|
||||||
|
m_customizableDock->reset();
|
||||||
|
m_customizableDock->dock(ui::LEFT, m_colorBar);
|
||||||
|
m_customizableDock->center()->dock(ui::TOP, m_contextBar);
|
||||||
|
m_customizableDock->center()->dock(ui::RIGHT, m_toolBar);
|
||||||
|
m_customizableDock->center()->center()->dock(ui::BOTTOM,
|
||||||
|
m_timeline,
|
||||||
|
gfx::Size(64 * guiscale(), 64 * guiscale()));
|
||||||
|
m_customizableDock->center()->center()->dock(ui::CENTER, m_workspace);
|
||||||
|
|
||||||
|
layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setDefaultMirrorLayout()
|
||||||
|
{
|
||||||
|
m_customizableDock->reset();
|
||||||
|
m_customizableDock->dock(ui::RIGHT, m_colorBar);
|
||||||
|
m_customizableDock->center()->dock(ui::TOP, m_contextBar);
|
||||||
|
m_customizableDock->center()->dock(ui::LEFT, m_toolBar);
|
||||||
|
m_customizableDock->center()->center()->dock(ui::BOTTOM,
|
||||||
|
m_timeline,
|
||||||
|
gfx::Size(64 * guiscale(), 64 * guiscale()));
|
||||||
|
m_customizableDock->center()->center()->dock(ui::CENTER, m_workspace);
|
||||||
|
|
||||||
|
layout();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::dataRecoverySessionsAreReady()
|
void MainWindow::dataRecoverySessionsAreReady()
|
||||||
{
|
{
|
||||||
getHomeView()->dataRecoverySessionsAreReady();
|
getHomeView()->dataRecoverySessionsAreReady();
|
||||||
|
@ -375,24 +417,15 @@ bool MainWindow::onProcessMessage(ui::Message* msg)
|
||||||
|
|
||||||
void MainWindow::onInitTheme(ui::InitThemeEvent& ev)
|
void MainWindow::onInitTheme(ui::InitThemeEvent& ev)
|
||||||
{
|
{
|
||||||
app::gen::MainWindow::onInitTheme(ev);
|
ui::Window::onInitTheme(ev);
|
||||||
|
noBorderNoChildSpacing();
|
||||||
if (m_previewEditor)
|
if (m_previewEditor)
|
||||||
m_previewEditor->initTheme();
|
m_previewEditor->initTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onSaveLayout(SaveLayoutEvent& ev)
|
|
||||||
{
|
|
||||||
// Invert the timeline splitter position before we save the setting.
|
|
||||||
if (Preferences::instance().general.timelinePosition() == gen::TimelinePosition::LEFT) {
|
|
||||||
timelineSplitter()->setPosition(100 - timelineSplitter()->getPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
Window::onSaveLayout(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onResize(ui::ResizeEvent& ev)
|
void MainWindow::onResize(ui::ResizeEvent& ev)
|
||||||
{
|
{
|
||||||
app::gen::MainWindow::onResize(ev);
|
ui::Window::onResize(ev);
|
||||||
|
|
||||||
os::Window* nativeWindow = (display() ? display()->nativeWindow() : nullptr);
|
os::Window* nativeWindow = (display() ? display()->nativeWindow() : nullptr);
|
||||||
if (nativeWindow && nativeWindow->screen()) {
|
if (nativeWindow && nativeWindow->screen()) {
|
||||||
|
@ -593,16 +626,21 @@ void MainWindow::configureWorkspaceLayout()
|
||||||
bool isDoc = (getDocView() != nullptr);
|
bool isDoc = (getDocView() != nullptr);
|
||||||
|
|
||||||
if (os::System::instance()->menus() == nullptr || pref.general.showMenuBar()) {
|
if (os::System::instance()->menus() == nullptr || pref.general.showMenuBar()) {
|
||||||
m_menuBar->resetMaxSize();
|
if (!m_menuBar->parent())
|
||||||
|
m_dock->top()->dock(CENTER, m_menuBar);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_menuBar->setMaxSize(gfx::Size(0, 0));
|
if (m_menuBar->parent())
|
||||||
|
m_dock->undock(m_menuBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_menuBar->setVisible(normal);
|
m_menuBar->setVisible(normal);
|
||||||
m_notifications->setVisible(normal && m_notifications->hasNotifications());
|
m_notifications->setVisible(normal && m_notifications->hasNotifications());
|
||||||
m_tabsBar->setVisible(normal);
|
m_tabsBar->setVisible(normal);
|
||||||
colorBarPlaceholder()->setVisible(normal && isDoc);
|
|
||||||
|
// TODO set visibility of color bar widgets
|
||||||
|
m_colorBar->setVisible(normal && isDoc);
|
||||||
|
|
||||||
m_toolBar->setVisible(normal && isDoc);
|
m_toolBar->setVisible(normal && isDoc);
|
||||||
m_statusBar->setVisible(normal);
|
m_statusBar->setVisible(normal);
|
||||||
m_contextBar->setVisible(isDoc && (m_mode == NormalMode || m_mode == ContextBarAndTimelineMode));
|
m_contextBar->setVisible(isDoc && (m_mode == NormalMode || m_mode == ContextBarAndTimelineMode));
|
||||||
|
@ -610,41 +648,22 @@ void MainWindow::configureWorkspaceLayout()
|
||||||
// Configure timeline
|
// Configure timeline
|
||||||
{
|
{
|
||||||
auto timelinePosition = pref.general.timelinePosition();
|
auto timelinePosition = pref.general.timelinePosition();
|
||||||
bool invertWidgets = false;
|
int side = ui::BOTTOM;
|
||||||
int align = VERTICAL;
|
|
||||||
|
m_customizableDock->undock(m_timeline);
|
||||||
|
|
||||||
switch (timelinePosition) {
|
switch (timelinePosition) {
|
||||||
case gen::TimelinePosition::LEFT:
|
case gen::TimelinePosition::LEFT: side = ui::LEFT; break;
|
||||||
align = HORIZONTAL;
|
case gen::TimelinePosition::RIGHT: side = ui::RIGHT; break;
|
||||||
invertWidgets = true;
|
case gen::TimelinePosition::BOTTOM: side = ui::BOTTOM; break;
|
||||||
break;
|
|
||||||
case gen::TimelinePosition::RIGHT: align = HORIZONTAL; break;
|
|
||||||
case gen::TimelinePosition::BOTTOM: break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timelineSplitter()->setAlign(align);
|
m_customizableDock->center()->center()->dock(side,
|
||||||
timelinePlaceholder()->setVisible(
|
m_timeline,
|
||||||
isDoc && (m_mode == NormalMode || m_mode == ContextBarAndTimelineMode) &&
|
gfx::Size(64 * guiscale(), 64 * guiscale()));
|
||||||
pref.general.visibleTimeline());
|
|
||||||
|
|
||||||
bool invertSplitterPos = false;
|
m_timeline->setVisible(isDoc && (m_mode == NormalMode || m_mode == ContextBarAndTimelineMode) &&
|
||||||
if (invertWidgets) {
|
pref.general.visibleTimeline());
|
||||||
if (timelineSplitter()->firstChild() == workspacePlaceholder() &&
|
|
||||||
timelineSplitter()->lastChild() == timelinePlaceholder()) {
|
|
||||||
timelineSplitter()->removeChild(workspacePlaceholder());
|
|
||||||
timelineSplitter()->addChild(workspacePlaceholder());
|
|
||||||
invertSplitterPos = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (timelineSplitter()->firstChild() == timelinePlaceholder() &&
|
|
||||||
timelineSplitter()->lastChild() == workspacePlaceholder()) {
|
|
||||||
timelineSplitter()->removeChild(timelinePlaceholder());
|
|
||||||
timelineSplitter()->addChild(timelinePlaceholder());
|
|
||||||
invertSplitterPos = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (invertSplitterPos)
|
|
||||||
timelineSplitter()->setPosition(100 - timelineSplitter()->getPosition());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_contextBar->isVisible()) {
|
if (m_contextBar->isVisible()) {
|
||||||
|
@ -652,7 +671,6 @@ void MainWindow::configureWorkspaceLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
layout();
|
layout();
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "app/ui/tabs.h"
|
#include "app/ui/tabs.h"
|
||||||
#include "ui/window.h"
|
#include "ui/window.h"
|
||||||
|
|
||||||
#include "main_window.xml.h"
|
#include <memory>
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class Splitter;
|
class Splitter;
|
||||||
|
@ -30,13 +30,16 @@ class ColorBar;
|
||||||
class ContextBar;
|
class ContextBar;
|
||||||
class DevConsoleView;
|
class DevConsoleView;
|
||||||
class DocView;
|
class DocView;
|
||||||
|
class Dock;
|
||||||
class HomeView;
|
class HomeView;
|
||||||
class INotificationDelegate;
|
class INotificationDelegate;
|
||||||
class MainMenuBar;
|
class MainMenuBar;
|
||||||
|
class LayoutSelector;
|
||||||
class Notifications;
|
class Notifications;
|
||||||
class PreviewEditorWindow;
|
class PreviewEditorWindow;
|
||||||
class StatusBar;
|
class StatusBar;
|
||||||
class Timeline;
|
class Timeline;
|
||||||
|
class ToolBar;
|
||||||
class Workspace;
|
class Workspace;
|
||||||
class WorkspaceTabs;
|
class WorkspaceTabs;
|
||||||
|
|
||||||
|
@ -44,7 +47,7 @@ namespace crash {
|
||||||
class DataRecovery;
|
class DataRecovery;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainWindow : public app::gen::MainWindow,
|
class MainWindow : public ui::Window,
|
||||||
public TabsDelegate {
|
public TabsDelegate {
|
||||||
public:
|
public:
|
||||||
enum Mode { NormalMode, ContextBarAndTimelineMode, EditorOnlyMode };
|
enum Mode { NormalMode, ContextBarAndTimelineMode, EditorOnlyMode };
|
||||||
|
@ -83,6 +86,9 @@ public:
|
||||||
void setTimelineVisibility(bool visible);
|
void setTimelineVisibility(bool visible);
|
||||||
void popTimeline();
|
void popTimeline();
|
||||||
|
|
||||||
|
void setDefaultLayout();
|
||||||
|
void setDefaultMirrorLayout();
|
||||||
|
|
||||||
// When crash::DataRecovery finish to search for sessions, this
|
// When crash::DataRecovery finish to search for sessions, this
|
||||||
// function is called.
|
// function is called.
|
||||||
void dataRecoverySessionsAreReady();
|
void dataRecoverySessionsAreReady();
|
||||||
|
@ -109,7 +115,6 @@ public:
|
||||||
protected:
|
protected:
|
||||||
bool onProcessMessage(ui::Message* msg) override;
|
bool onProcessMessage(ui::Message* msg) override;
|
||||||
void onInitTheme(ui::InitThemeEvent& ev) override;
|
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||||
void onSaveLayout(ui::SaveLayoutEvent& ev) override;
|
|
||||||
void onResize(ui::ResizeEvent& ev) override;
|
void onResize(ui::ResizeEvent& ev) override;
|
||||||
void onBeforeViewChange();
|
void onBeforeViewChange();
|
||||||
void onActiveViewChange();
|
void onActiveViewChange();
|
||||||
|
@ -123,11 +128,14 @@ private:
|
||||||
void configureWorkspaceLayout();
|
void configureWorkspaceLayout();
|
||||||
|
|
||||||
ui::TooltipManager* m_tooltipManager;
|
ui::TooltipManager* m_tooltipManager;
|
||||||
|
Dock* m_dock;
|
||||||
|
Dock* m_customizableDock;
|
||||||
MainMenuBar* m_menuBar;
|
MainMenuBar* m_menuBar;
|
||||||
|
LayoutSelector* m_layoutSelector;
|
||||||
StatusBar* m_statusBar;
|
StatusBar* m_statusBar;
|
||||||
ColorBar* m_colorBar;
|
ColorBar* m_colorBar;
|
||||||
ContextBar* m_contextBar;
|
ContextBar* m_contextBar;
|
||||||
ui::Widget* m_toolBar;
|
ToolBar* m_toolBar;
|
||||||
WorkspaceTabs* m_tabsBar;
|
WorkspaceTabs* m_tabsBar;
|
||||||
Mode m_mode;
|
Mode m_mode;
|
||||||
Timeline* m_timeline;
|
Timeline* m_timeline;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#define APP_UI_NOTIFICATIONS_H_INCLUDED
|
#define APP_UI_NOTIFICATIONS_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "ui/button.h"
|
#include "ui/button.h"
|
||||||
#include "ui/menu.h"
|
#include "ui/menu.h"
|
||||||
|
|
||||||
|
@ -19,13 +20,17 @@ class Style;
|
||||||
namespace app {
|
namespace app {
|
||||||
class INotificationDelegate;
|
class INotificationDelegate;
|
||||||
|
|
||||||
class Notifications : public ui::Button {
|
class Notifications : public ui::Button,
|
||||||
|
public Dockable {
|
||||||
public:
|
public:
|
||||||
Notifications();
|
Notifications();
|
||||||
|
|
||||||
void addLink(INotificationDelegate* del);
|
void addLink(INotificationDelegate* del);
|
||||||
bool hasNotifications() const { return m_popup.hasChildren(); }
|
bool hasNotifications() const { return m_popup.hasChildren(); }
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override { return ui::TOP | ui::BOTTOM | ui::LEFT | ui::RIGHT; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onSizeHint(ui::SizeHintEvent& ev) override;
|
void onSizeHint(ui::SizeHintEvent& ev) override;
|
||||||
void onPaint(ui::PaintEvent& ev) override;
|
void onPaint(ui::PaintEvent& ev) override;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "app/context_observer.h"
|
#include "app/context_observer.h"
|
||||||
#include "app/tools/active_tool_observer.h"
|
#include "app/tools/active_tool_observer.h"
|
||||||
#include "app/ui/doc_observer_widget.h"
|
#include "app/ui/doc_observer_widget.h"
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "base/time.h"
|
#include "base/time.h"
|
||||||
#include "doc/tile.h"
|
#include "doc/tile.h"
|
||||||
#include "ui/base.h"
|
#include "ui/base.h"
|
||||||
|
@ -44,7 +45,8 @@ class Tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatusBar : public DocObserverWidget<ui::HBox>,
|
class StatusBar : public DocObserverWidget<ui::HBox>,
|
||||||
public tools::ActiveToolObserver {
|
public tools::ActiveToolObserver,
|
||||||
|
public Dockable {
|
||||||
static StatusBar* m_instance;
|
static StatusBar* m_instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -72,6 +74,9 @@ public:
|
||||||
|
|
||||||
void showBackupIcon(BackupIcon icon);
|
void showBackupIcon(BackupIcon icon);
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onInitTheme(ui::InitThemeEvent& ev) override;
|
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||||
void onResize(ui::ResizeEvent& ev) override;
|
void onResize(ui::ResizeEvent& ev) override;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#define APP_UI_TABS_H_INCLUDED
|
#define APP_UI_TABS_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "base/ref.h"
|
#include "base/ref.h"
|
||||||
#include "text/fwd.h"
|
#include "text/fwd.h"
|
||||||
#include "ui/animated_widget.h"
|
#include "ui/animated_widget.h"
|
||||||
|
@ -118,7 +119,8 @@ public:
|
||||||
|
|
||||||
// Tabs control. Used to show opened documents.
|
// Tabs control. Used to show opened documents.
|
||||||
class Tabs : public ui::Widget,
|
class Tabs : public ui::Widget,
|
||||||
public ui::AnimatedWidget {
|
public ui::AnimatedWidget,
|
||||||
|
public Dockable {
|
||||||
struct Tab {
|
struct Tab {
|
||||||
TabView* view;
|
TabView* view;
|
||||||
std::string text;
|
std::string text;
|
||||||
|
@ -181,6 +183,9 @@ public:
|
||||||
void removeDropViewPreview();
|
void removeDropViewPreview();
|
||||||
int getDropTabIndex() const { return m_dropNewIndex; }
|
int getDropTabIndex() const { return m_dropNewIndex; }
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool onProcessMessage(ui::Message* msg) override;
|
bool onProcessMessage(ui::Message* msg) override;
|
||||||
void onInitTheme(ui::InitThemeEvent& ev) override;
|
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "app/docs_observer.h"
|
#include "app/docs_observer.h"
|
||||||
#include "app/loop_tag.h"
|
#include "app/loop_tag.h"
|
||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "app/ui/editor/editor_observer.h"
|
#include "app/ui/editor/editor_observer.h"
|
||||||
#include "app/ui/input_chain_element.h"
|
#include "app/ui/input_chain_element.h"
|
||||||
#include "app/ui/timeline/ani_controls.h"
|
#include "app/ui/timeline/ani_controls.h"
|
||||||
|
@ -73,7 +74,8 @@ class Timeline : public ui::Widget,
|
||||||
public DocObserver,
|
public DocObserver,
|
||||||
public EditorObserver,
|
public EditorObserver,
|
||||||
public InputChainElement,
|
public InputChainElement,
|
||||||
public TagProvider {
|
public TagProvider,
|
||||||
|
public Dockable {
|
||||||
public:
|
public:
|
||||||
using Range = view::Range;
|
using Range = view::Range;
|
||||||
using RealRange = view::RealRange;
|
using RealRange = view::RealRange;
|
||||||
|
@ -155,6 +157,12 @@ public:
|
||||||
|
|
||||||
void clearAndInvalidateRange();
|
void clearAndInvalidateRange();
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override
|
||||||
|
{
|
||||||
|
return ui::TOP | ui::BOTTOM | ui::LEFT | ui::RIGHT | ui::EXPANSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool onProcessMessage(ui::Message* msg) override;
|
bool onProcessMessage(ui::Message* msg) override;
|
||||||
void onInitTheme(ui::InitThemeEvent& ev) override;
|
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2025 Igara Studio S.A.
|
// Copyright (C) 2021-2025 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
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "app/tools/active_tool_observer.h"
|
#include "app/tools/active_tool_observer.h"
|
||||||
|
#include "app/ui/dockable.h"
|
||||||
#include "app/ui/skin/skin_part.h"
|
#include "app/ui/skin/skin_part.h"
|
||||||
#include "gfx/point.h"
|
#include "gfx/point.h"
|
||||||
#include "obs/connection.h"
|
#include "obs/connection.h"
|
||||||
|
@ -32,6 +33,7 @@ class ToolGroup;
|
||||||
|
|
||||||
// Class to show selected tools for each tool (vertically)
|
// Class to show selected tools for each tool (vertically)
|
||||||
class ToolBar : public ui::Widget,
|
class ToolBar : public ui::Widget,
|
||||||
|
public Dockable,
|
||||||
public tools::ActiveToolObserver {
|
public tools::ActiveToolObserver {
|
||||||
static ToolBar* m_instance;
|
static ToolBar* m_instance;
|
||||||
|
|
||||||
|
@ -52,6 +54,14 @@ public:
|
||||||
void openTipWindow(tools::ToolGroup* toolGroup, tools::Tool* tool);
|
void openTipWindow(tools::ToolGroup* toolGroup, tools::Tool* tool);
|
||||||
void closeTipWindow();
|
void closeTipWindow();
|
||||||
|
|
||||||
|
// Dockable impl
|
||||||
|
int dockableAt() const override
|
||||||
|
{
|
||||||
|
// TODO add future support to dock the tool bar at the
|
||||||
|
// top/bottom sides
|
||||||
|
return ui::LEFT | ui::RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool onProcessMessage(ui::Message* msg) override;
|
bool onProcessMessage(ui::Message* msg) override;
|
||||||
void onSizeHint(ui::SizeHintEvent& ev) override;
|
void onSizeHint(ui::SizeHintEvent& ev) override;
|
||||||
|
|
Loading…
Reference in New Issue