mirror of https://github.com/aseprite/aseprite.git
Save layout changes immediately when docks are resized
This patch includes: * The layout is updated immediately when docks are resized (before this the layout was like a snapshot of the configuration when the layout was created or overwritten with the "New Layout" option) * Saving the active layout used in preferences.general.workspace_layout so we can restore it after restarting Aseprite * Change "UI Layout" to "Workspace Layout" * Some strings moved to en.ini file for i18n * Fixed a crash on MainWindow::onActiveViewChange() when the application was being closed
This commit is contained in:
parent
eebd7c2666
commit
00b0ba93d1
|
@ -167,6 +167,7 @@
|
||||||
<option id="keep_closed_sprite_on_memory_for" type="double" default="15.0" />
|
<option id="keep_closed_sprite_on_memory_for" type="double" default="15.0" />
|
||||||
<option id="show_full_path" type="bool" default="true" />
|
<option id="show_full_path" type="bool" default="true" />
|
||||||
<option id="edit_full_path" type="bool" default="false" />
|
<option id="edit_full_path" type="bool" default="false" />
|
||||||
|
<option id="workspace_layout" type="std::string" />
|
||||||
<option id="timeline_position" type="TimelinePosition" default="TimelinePosition::BOTTOM" />
|
<option id="timeline_position" type="TimelinePosition" default="TimelinePosition::BOTTOM" />
|
||||||
<option id="timeline_layer_panel_width" type="int" default="100" />
|
<option id="timeline_layer_panel_width" type="int" default="100" />
|
||||||
<option id="show_menu_bar" type="bool" default="true" />
|
<option id="show_menu_bar" type="bool" default="true" />
|
||||||
|
|
|
@ -1208,7 +1208,12 @@ help_enter_license = Enter &License
|
||||||
help_about = &About
|
help_about = &About
|
||||||
|
|
||||||
[main_window]
|
[main_window]
|
||||||
layout = User Interface Layout
|
layout = Workspace Layout
|
||||||
|
default_layout = Default
|
||||||
|
mirrored_default_layout = Mirrored Default
|
||||||
|
timeline = Timeline
|
||||||
|
user_layouts = User Layouts
|
||||||
|
new_layout = New Layout...
|
||||||
|
|
||||||
[mask_by_color]
|
[mask_by_color]
|
||||||
title = Select Color
|
title = Select Color
|
||||||
|
@ -1239,9 +1244,9 @@ tileset = Tileset:
|
||||||
default_new_layer_name = New Layer
|
default_new_layer_name = New Layer
|
||||||
|
|
||||||
[new_layout]
|
[new_layout]
|
||||||
title = New UI Layout
|
title = New Workspace Layout
|
||||||
name = Name:
|
name = Name:
|
||||||
default_name = User Layout
|
default_name = User Layout {}
|
||||||
|
|
||||||
[news_listbox]
|
[news_listbox]
|
||||||
more = More...
|
more = More...
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2021-2022 Igara Studio S.A.
|
// Copyright (C) 2021-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
|
@ -349,6 +349,7 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
||||||
case kMouseUpMessage: {
|
case kMouseUpMessage: {
|
||||||
if (hasCapture()) {
|
if (hasCapture()) {
|
||||||
releaseMouse();
|
releaseMouse();
|
||||||
|
onUserResizedDock();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -377,6 +378,20 @@ bool Dock::onProcessMessage(ui::Message* msg)
|
||||||
return Widget::onProcessMessage(msg);
|
return Widget::onProcessMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Dock::onUserResizedDock()
|
||||||
|
{
|
||||||
|
// Generate the UserResizedDock signal, this can be used to know
|
||||||
|
// when the user modified the dock configuration to save the new
|
||||||
|
// layout in a user/preference file.
|
||||||
|
UserResizedDock();
|
||||||
|
|
||||||
|
// Send the same notification for the parent (as probably eh
|
||||||
|
// MainWindow is listening the signal of just the root dock).
|
||||||
|
if (auto parentDock = dynamic_cast<Dock*>(parent())) {
|
||||||
|
parentDock->onUserResizedDock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Dock::setSide(const int i, Widget* newWidget)
|
void Dock::setSide(const int i, Widget* newWidget)
|
||||||
{
|
{
|
||||||
m_sides[i] = newWidget;
|
m_sides[i] = newWidget;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2021-2022 Igara Studio S.A.
|
// Copyright (C) 2021-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
|
@ -58,6 +58,7 @@ public:
|
||||||
gfx::Size getUserDefinedSizeAtSide(int side) const;
|
gfx::Size getUserDefinedSizeAtSide(int side) const;
|
||||||
|
|
||||||
obs::signal<void()> Resize;
|
obs::signal<void()> Resize;
|
||||||
|
obs::signal<void()> UserResizedDock;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onSizeHint(ui::SizeHintEvent& ev) override;
|
void onSizeHint(ui::SizeHintEvent& ev) override;
|
||||||
|
@ -65,6 +66,7 @@ protected:
|
||||||
void onPaint(ui::PaintEvent& ev) override;
|
void onPaint(ui::PaintEvent& ev) override;
|
||||||
void onInitTheme(ui::InitThemeEvent& ev) override;
|
void onInitTheme(ui::InitThemeEvent& ev) override;
|
||||||
bool onProcessMessage(ui::Message* msg) override;
|
bool onProcessMessage(ui::Message* msg) override;
|
||||||
|
void onUserResizedDock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setSide(const int i, ui::Widget* newWidget);
|
void setSide(const int i, ui::Widget* newWidget);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2022 Igara Studio S.A.
|
// Copyright (C) 2022-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
|
@ -144,17 +144,20 @@ static void load_dock_layout(const TiXmlElement* elem, Dock* dock)
|
||||||
LayoutPtr Layout::MakeFromXmlElement(const TiXmlElement* layoutElem)
|
LayoutPtr Layout::MakeFromXmlElement(const TiXmlElement* layoutElem)
|
||||||
{
|
{
|
||||||
auto layout = std::make_shared<Layout>();
|
auto layout = std::make_shared<Layout>();
|
||||||
if (auto name = layoutElem->Attribute("name"))
|
if (auto name = layoutElem->Attribute("name")) {
|
||||||
|
layout->m_id = name;
|
||||||
layout->m_name = name;
|
layout->m_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
layout->m_elem.reset(layoutElem->Clone()->ToElement());
|
layout->m_elem.reset(layoutElem->Clone()->ToElement());
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
LayoutPtr Layout::MakeFromDock(const std::string& name, const Dock* dock)
|
LayoutPtr Layout::MakeFromDock(const std::string& id, const std::string& name, const Dock* dock)
|
||||||
{
|
{
|
||||||
auto layout = std::make_shared<Layout>();
|
auto layout = std::make_shared<Layout>();
|
||||||
|
layout->m_id = id;
|
||||||
layout->m_name = name;
|
layout->m_name = name;
|
||||||
|
|
||||||
layout->m_elem = std::make_unique<TiXmlElement>("layout");
|
layout->m_elem = std::make_unique<TiXmlElement>("layout");
|
||||||
|
@ -164,6 +167,16 @@ LayoutPtr Layout::MakeFromDock(const std::string& name, const Dock* dock)
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Layout::matchId(const std::string& id) const
|
||||||
|
{
|
||||||
|
if (m_id == id)
|
||||||
|
return true;
|
||||||
|
else if ((m_id.empty() && id == kDefault) || (m_id == kDefault && id.empty()))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Layout::loadLayout(Dock* dock) const
|
bool Layout::loadLayout(Dock* dock) const
|
||||||
{
|
{
|
||||||
if (!m_elem)
|
if (!m_elem)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2022 Igara Studio S.A.
|
// Copyright (C) 2022-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
|
@ -21,15 +21,21 @@ using LayoutPtr = std::shared_ptr<Layout>;
|
||||||
|
|
||||||
class Layout final {
|
class Layout final {
|
||||||
public:
|
public:
|
||||||
static LayoutPtr MakeFromXmlElement(const TiXmlElement* layoutElem);
|
static constexpr const char* kDefault = "_default_";
|
||||||
static LayoutPtr MakeFromDock(const std::string& name, const Dock* dock);
|
static constexpr const char* kMirroredDefault = "_mirrored_default_";
|
||||||
|
|
||||||
|
static LayoutPtr MakeFromXmlElement(const TiXmlElement* layoutElem);
|
||||||
|
static LayoutPtr MakeFromDock(const std::string& id, const std::string& name, const Dock* dock);
|
||||||
|
|
||||||
|
const std::string& id() const { return m_id; }
|
||||||
const std::string& name() const { return m_name; }
|
const std::string& name() const { return m_name; }
|
||||||
const TiXmlElement* xmlElement() const { return m_elem.get(); }
|
const TiXmlElement* xmlElement() const { return m_elem.get(); }
|
||||||
|
|
||||||
|
bool matchId(const std::string& id) const;
|
||||||
bool loadLayout(Dock* dock) const;
|
bool loadLayout(Dock* dock) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string m_id;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::unique_ptr<TiXmlElement> m_elem;
|
std::unique_ptr<TiXmlElement> m_elem;
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "app/ui/main_window.h"
|
#include "app/ui/main_window.h"
|
||||||
#include "app/ui/separator_in_view.h"
|
#include "app/ui/separator_in_view.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "fmt/format.h"
|
|
||||||
#include "ui/entry.h"
|
#include "ui/entry.h"
|
||||||
#include "ui/listitem.h"
|
#include "ui/listitem.h"
|
||||||
#include "ui/tooltips.h"
|
#include "ui/tooltips.h"
|
||||||
|
@ -127,26 +126,35 @@ private:
|
||||||
|
|
||||||
class LayoutSelector::LayoutItem : public ListItem {
|
class LayoutSelector::LayoutItem : public ListItem {
|
||||||
public:
|
public:
|
||||||
enum LayoutId {
|
enum LayoutOption {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
DEFAULT_MIRROR,
|
MIRRORED_DEFAULT,
|
||||||
SAVE_LAYOUT,
|
|
||||||
USER_DEFINED,
|
USER_DEFINED,
|
||||||
|
NEW_LAYOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
LayoutItem(LayoutSelector* selector,
|
LayoutItem(LayoutSelector* selector,
|
||||||
const LayoutId id,
|
const LayoutOption option,
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
const LayoutPtr layout = nullptr)
|
const LayoutPtr layout)
|
||||||
: ListItem(text)
|
: ListItem(text)
|
||||||
, m_id(id)
|
, m_option(option)
|
||||||
, m_selector(selector)
|
, m_selector(selector)
|
||||||
, m_layout(layout)
|
, m_layout(layout)
|
||||||
{
|
{
|
||||||
ASSERT((id != USER_DEFINED && layout == nullptr) || (id == USER_DEFINED && layout != nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout* layout() const { return m_layout.get(); }
|
std::string getLayoutId() const
|
||||||
|
{
|
||||||
|
if (m_layout)
|
||||||
|
return m_layout->id();
|
||||||
|
else
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matchId(const std::string& id) const { return (m_layout && m_layout->matchId(id)); }
|
||||||
|
|
||||||
|
const LayoutPtr& layout() const { return m_layout; }
|
||||||
|
|
||||||
void setLayout(const LayoutPtr& layout) { m_layout = layout; }
|
void setLayout(const LayoutPtr& layout) { m_layout = layout; }
|
||||||
|
|
||||||
|
@ -154,26 +162,25 @@ public:
|
||||||
{
|
{
|
||||||
MainWindow* win = App::instance()->mainWindow();
|
MainWindow* win = App::instance()->mainWindow();
|
||||||
|
|
||||||
switch (m_id) {
|
if (m_layout)
|
||||||
case LayoutId::DEFAULT: win->setDefaultLayout(); break;
|
m_selector->m_activeLayoutId = m_layout->id();
|
||||||
case LayoutId::DEFAULT_MIRROR: win->setDefaultMirrorLayout(); break;
|
|
||||||
case LayoutId::USER_DEFINED:
|
switch (m_option) {
|
||||||
ASSERT(m_layout);
|
case LayoutOption::DEFAULT: win->setDefaultLayout(); break;
|
||||||
if (m_layout)
|
case LayoutOption::MIRRORED_DEFAULT: win->setMirroredDefaultLayout(); break;
|
||||||
win->loadUserLayout(m_layout.get());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Do nothing
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// Even Default & Mirrored Default can have a customized layout
|
||||||
|
// (customized default layout).
|
||||||
|
if (m_layout)
|
||||||
|
win->loadUserLayout(m_layout.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectAfterClose()
|
void selectAfterClose()
|
||||||
{
|
{
|
||||||
MainWindow* win = App::instance()->mainWindow();
|
MainWindow* win = App::instance()->mainWindow();
|
||||||
|
|
||||||
switch (m_id) {
|
switch (m_option) {
|
||||||
case LayoutId::SAVE_LAYOUT: {
|
case LayoutOption::NEW_LAYOUT: {
|
||||||
// Select the "Layout" separator (it's like selecting nothing)
|
// Select the "Layout" separator (it's like selecting nothing)
|
||||||
// TODO improve the ComboBox to select a real "nothing" (with
|
// TODO improve the ComboBox to select a real "nothing" (with
|
||||||
// a placeholder text)
|
// a placeholder text)
|
||||||
|
@ -183,14 +190,13 @@ public:
|
||||||
LayoutsEntry name(m_selector->m_layouts);
|
LayoutsEntry name(m_selector->m_layouts);
|
||||||
name.getEntryWidget()->setMaxTextLength(128);
|
name.getEntryWidget()->setMaxTextLength(128);
|
||||||
name.setFocusMagnet(true);
|
name.setFocusMagnet(true);
|
||||||
name.setValue(fmt::format("{} ({})",
|
name.setValue(Strings::new_layout_default_name(m_selector->m_layouts.size() + 1));
|
||||||
Strings::new_layout_default_name(),
|
|
||||||
m_selector->m_layouts.size() + 1));
|
|
||||||
|
|
||||||
window.namePlaceholder()->addChild(&name);
|
window.namePlaceholder()->addChild(&name);
|
||||||
window.openWindowInForeground();
|
window.openWindowInForeground();
|
||||||
if (window.closer() == window.ok()) {
|
if (window.closer() == window.ok()) {
|
||||||
auto layout = Layout::MakeFromDock(name.getValue(), win->customizableDock());
|
auto layout =
|
||||||
|
Layout::MakeFromDock(name.getValue(), name.getValue(), win->customizableDock());
|
||||||
|
|
||||||
m_selector->addLayout(layout);
|
m_selector->addLayout(layout);
|
||||||
}
|
}
|
||||||
|
@ -203,7 +209,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LayoutId m_id;
|
LayoutOption m_option;
|
||||||
LayoutSelector* m_selector;
|
LayoutSelector* m_selector;
|
||||||
LayoutPtr m_layout;
|
LayoutPtr m_layout;
|
||||||
};
|
};
|
||||||
|
@ -229,6 +235,8 @@ void LayoutSelector::LayoutComboBox::onCloseListBox()
|
||||||
LayoutSelector::LayoutSelector(TooltipManager* tooltipManager)
|
LayoutSelector::LayoutSelector(TooltipManager* tooltipManager)
|
||||||
: m_button(SkinTheme::instance()->parts.iconUserData())
|
: m_button(SkinTheme::instance()->parts.iconUserData())
|
||||||
{
|
{
|
||||||
|
m_activeLayoutId = Preferences::instance().general.workspaceLayout();
|
||||||
|
|
||||||
m_button.Click.connect([this]() { switchSelector(); });
|
m_button.Click.connect([this]() { switchSelector(); });
|
||||||
|
|
||||||
m_comboBox.setVisible(false);
|
m_comboBox.setVisible(false);
|
||||||
|
@ -248,16 +256,25 @@ LayoutSelector::LayoutSelector(TooltipManager* tooltipManager)
|
||||||
|
|
||||||
LayoutSelector::~LayoutSelector()
|
LayoutSelector::~LayoutSelector()
|
||||||
{
|
{
|
||||||
|
Preferences::instance().general.workspaceLayout(m_activeLayoutId);
|
||||||
|
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LayoutPtr LayoutSelector::activeLayout()
|
||||||
|
{
|
||||||
|
return m_layouts.getById(m_activeLayoutId);
|
||||||
|
}
|
||||||
|
|
||||||
void LayoutSelector::addLayout(const LayoutPtr& layout)
|
void LayoutSelector::addLayout(const LayoutPtr& layout)
|
||||||
{
|
{
|
||||||
bool added = m_layouts.addLayout(layout);
|
bool added = m_layouts.addLayout(layout);
|
||||||
if (added) {
|
if (added) {
|
||||||
auto item = m_comboBox.addItem(
|
auto item = new LayoutItem(this, LayoutItem::USER_DEFINED, layout->name(), layout);
|
||||||
new LayoutItem(this, LayoutItem::USER_DEFINED, layout->name(), layout));
|
m_comboBox.insertItem(m_comboBox.getItemCount() - 1, // Above the "New Layout" item
|
||||||
m_comboBox.setSelectedItemIndex(item);
|
item);
|
||||||
|
|
||||||
|
m_comboBox.setSelectedItem(item);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (auto item : m_comboBox) {
|
for (auto item : m_comboBox) {
|
||||||
|
@ -272,6 +289,15 @@ void LayoutSelector::addLayout(const LayoutPtr& layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LayoutSelector::updateActiveLayout(const LayoutPtr& newLayout)
|
||||||
|
{
|
||||||
|
bool result = m_layouts.addLayout(newLayout);
|
||||||
|
|
||||||
|
// It means that the layout wasn't added, but replaced, when we
|
||||||
|
// update a layout it must be existent in the m_layouts collection.
|
||||||
|
ASSERT(result == false);
|
||||||
|
}
|
||||||
|
|
||||||
void LayoutSelector::onAnimationFrame()
|
void LayoutSelector::onAnimationFrame()
|
||||||
{
|
{
|
||||||
switch (animation()) {
|
switch (animation()) {
|
||||||
|
@ -321,16 +347,23 @@ void LayoutSelector::switchSelector()
|
||||||
|
|
||||||
// Create the combobox for first time
|
// Create the combobox for first time
|
||||||
if (m_comboBox.getItemCount() == 0) {
|
if (m_comboBox.getItemCount() == 0) {
|
||||||
m_comboBox.addItem(new SeparatorInView("Layout", HORIZONTAL));
|
m_comboBox.addItem(new SeparatorInView(Strings::main_window_layout(), HORIZONTAL));
|
||||||
m_comboBox.addItem(new LayoutItem(this, LayoutItem::DEFAULT, "Default"));
|
m_comboBox.addItem(new LayoutItem(this,
|
||||||
m_comboBox.addItem(new LayoutItem(this, LayoutItem::DEFAULT_MIRROR, "Default / Mirror"));
|
LayoutItem::DEFAULT,
|
||||||
m_comboBox.addItem(new SeparatorInView("Timeline", HORIZONTAL));
|
Strings::main_window_default_layout(),
|
||||||
|
m_layouts.getById(Layout::kDefault)));
|
||||||
|
m_comboBox.addItem(new LayoutItem(this,
|
||||||
|
LayoutItem::MIRRORED_DEFAULT,
|
||||||
|
Strings::main_window_mirrored_default_layout(),
|
||||||
|
m_layouts.getById(Layout::kMirroredDefault)));
|
||||||
|
m_comboBox.addItem(new SeparatorInView(Strings::main_window_timeline(), HORIZONTAL));
|
||||||
m_comboBox.addItem(new TimelineButtons());
|
m_comboBox.addItem(new TimelineButtons());
|
||||||
m_comboBox.addItem(new SeparatorInView("User Layouts", HORIZONTAL));
|
m_comboBox.addItem(new SeparatorInView(Strings::main_window_user_layouts(), HORIZONTAL));
|
||||||
m_comboBox.addItem(new LayoutItem(this, LayoutItem::SAVE_LAYOUT, "Save..."));
|
|
||||||
for (const auto& layout : m_layouts) {
|
for (const auto& layout : m_layouts) {
|
||||||
m_comboBox.addItem(new LayoutItem(this, LayoutItem::USER_DEFINED, layout->name(), layout));
|
m_comboBox.addItem(new LayoutItem(this, LayoutItem::USER_DEFINED, layout->name(), layout));
|
||||||
}
|
}
|
||||||
|
m_comboBox.addItem(
|
||||||
|
new LayoutItem(this, LayoutItem::NEW_LAYOUT, Strings::main_window_new_layout(), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_comboBox.setVisible(true);
|
m_comboBox.setVisible(true);
|
||||||
|
@ -344,6 +377,9 @@ void LayoutSelector::switchSelector()
|
||||||
m_endSize = gfx::Size(0, 0);
|
m_endSize = gfx::Size(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto item = getItemByLayoutId(m_activeLayoutId))
|
||||||
|
m_comboBox.setSelectedItem(item);
|
||||||
|
|
||||||
m_comboBox.setSizeHint(m_startSize);
|
m_comboBox.setSizeHint(m_startSize);
|
||||||
startAnimation((expand ? ANI_EXPANDING : ANI_COLLAPSING), ANI_TICKS);
|
startAnimation((expand ? ANI_EXPANDING : ANI_COLLAPSING), ANI_TICKS);
|
||||||
}
|
}
|
||||||
|
@ -364,4 +400,15 @@ void LayoutSelector::setupTooltips(TooltipManager* tooltipManager)
|
||||||
tooltipManager->addTooltipFor(&m_button, Strings::main_window_layout(), TOP);
|
tooltipManager->addTooltipFor(&m_button, Strings::main_window_layout(), TOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LayoutSelector::LayoutItem* LayoutSelector::getItemByLayoutId(const std::string& id)
|
||||||
|
{
|
||||||
|
for (auto child : m_comboBox) {
|
||||||
|
if (auto item = dynamic_cast<LayoutItem*>(child)) {
|
||||||
|
if (item->matchId(id))
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|
|
@ -47,7 +47,11 @@ public:
|
||||||
LayoutSelector(ui::TooltipManager* tooltipManager);
|
LayoutSelector(ui::TooltipManager* tooltipManager);
|
||||||
~LayoutSelector();
|
~LayoutSelector();
|
||||||
|
|
||||||
|
LayoutPtr activeLayout();
|
||||||
|
std::string activeLayoutId() const { return m_activeLayoutId; }
|
||||||
|
|
||||||
void addLayout(const LayoutPtr& layout);
|
void addLayout(const LayoutPtr& layout);
|
||||||
|
void updateActiveLayout(const LayoutPtr& layout);
|
||||||
void switchSelector();
|
void switchSelector();
|
||||||
void switchSelectorFromCommand();
|
void switchSelectorFromCommand();
|
||||||
bool isSelectorVisible() const;
|
bool isSelectorVisible() const;
|
||||||
|
@ -57,9 +61,11 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupTooltips(ui::TooltipManager* tooltipManager);
|
void setupTooltips(ui::TooltipManager* tooltipManager);
|
||||||
|
LayoutItem* getItemByLayoutId(const std::string& id);
|
||||||
void onAnimationFrame() override;
|
void onAnimationFrame() override;
|
||||||
void onAnimationStop(int animation) override;
|
void onAnimationStop(int animation) override;
|
||||||
|
|
||||||
|
std::string m_activeLayoutId;
|
||||||
LayoutComboBox m_comboBox;
|
LayoutComboBox m_comboBox;
|
||||||
IconButton m_button;
|
IconButton m_button;
|
||||||
gfx::Size m_startSize;
|
gfx::Size m_startSize;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (c) 2022 Igara Studio S.A.
|
// Copyright (c) 2022-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
|
@ -38,10 +38,18 @@ Layouts::~Layouts()
|
||||||
save(m_userLayoutsFilename);
|
save(m_userLayoutsFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LayoutPtr Layouts::getById(const std::string& id) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_layouts.begin(), m_layouts.end(), [&id](const LayoutPtr& l) {
|
||||||
|
return l->matchId(id);
|
||||||
|
});
|
||||||
|
return (it != m_layouts.end() ? *it : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
bool Layouts::addLayout(const LayoutPtr& layout)
|
bool Layouts::addLayout(const LayoutPtr& layout)
|
||||||
{
|
{
|
||||||
auto it = std::find_if(m_layouts.begin(), m_layouts.end(), [layout](const LayoutPtr& l) {
|
auto it = std::find_if(m_layouts.begin(), m_layouts.end(), [layout](const LayoutPtr& l) {
|
||||||
return l->name() == layout->name();
|
return l->matchId(layout->id());
|
||||||
});
|
});
|
||||||
if (it != m_layouts.end()) {
|
if (it != m_layouts.end()) {
|
||||||
*it = layout; // Replace existent layout
|
*it = layout; // Replace existent layout
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (c) 2022 Igara Studio S.A.
|
// Copyright (c) 2022-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
|
@ -22,6 +22,8 @@ public:
|
||||||
|
|
||||||
size_t size() const { return m_layouts.size(); }
|
size_t size() const { return m_layouts.size(); }
|
||||||
|
|
||||||
|
LayoutPtr getById(const std::string& id) const;
|
||||||
|
|
||||||
// Returns true if the layout is added, or false if it was
|
// Returns true if the layout is added, or false if it was
|
||||||
// replaced.
|
// replaced.
|
||||||
bool addLayout(const LayoutPtr& layout);
|
bool addLayout(const LayoutPtr& layout);
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "os/event.h"
|
#include "os/event.h"
|
||||||
#include "os/event_queue.h"
|
#include "os/event_queue.h"
|
||||||
#include "os/system.h"
|
#include "os/system.h"
|
||||||
|
#include "ui/app_state.h"
|
||||||
#include "ui/drag_event.h"
|
#include "ui/drag_event.h"
|
||||||
#include "ui/message.h"
|
#include "ui/message.h"
|
||||||
#include "ui/splitter.h"
|
#include "ui/splitter.h"
|
||||||
|
@ -183,7 +184,13 @@ void MainWindow::initialize()
|
||||||
m_dock->top()->dock(ui::CENTER, m_menuBar.get());
|
m_dock->top()->dock(ui::CENTER, m_menuBar.get());
|
||||||
m_dock->dock(ui::CENTER, m_customizableDockPlaceholder.get());
|
m_dock->dock(ui::CENTER, m_customizableDockPlaceholder.get());
|
||||||
|
|
||||||
|
// After the user resizes the dock we save the updated layout
|
||||||
|
m_saveDockLayoutConn = m_customizableDock->UserResizedDock.connect(
|
||||||
|
[this] { saveActiveLayout(); });
|
||||||
|
|
||||||
setDefaultLayout();
|
setDefaultLayout();
|
||||||
|
if (LayoutPtr layout = m_layoutSelector->activeLayout())
|
||||||
|
loadUserLayout(layout.get());
|
||||||
|
|
||||||
// Reconfigure workspace when the timeline position is changed.
|
// Reconfigure workspace when the timeline position is changed.
|
||||||
auto& pref = Preferences::instance();
|
auto& pref = Preferences::instance();
|
||||||
|
@ -204,6 +211,7 @@ MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
m_timelineResizeConn.disconnect();
|
m_timelineResizeConn.disconnect();
|
||||||
m_colorBarResizeConn.disconnect();
|
m_colorBarResizeConn.disconnect();
|
||||||
|
m_saveDockLayoutConn.disconnect();
|
||||||
|
|
||||||
m_dock->resetDocks();
|
m_dock->resetDocks();
|
||||||
m_customizableDock->resetDocks();
|
m_customizableDock->resetDocks();
|
||||||
|
@ -408,7 +416,7 @@ void MainWindow::setDefaultLayout()
|
||||||
configureWorkspaceLayout();
|
configureWorkspaceLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setDefaultMirrorLayout()
|
void MainWindow::setMirroredDefaultLayout()
|
||||||
{
|
{
|
||||||
m_timelineResizeConn.disconnect();
|
m_timelineResizeConn.disconnect();
|
||||||
m_colorBarResizeConn.disconnect();
|
m_colorBarResizeConn.disconnect();
|
||||||
|
@ -495,6 +503,11 @@ void MainWindow::onBeforeViewChange()
|
||||||
// inform to the UIContext that the current view has changed.
|
// inform to the UIContext that the current view has changed.
|
||||||
void MainWindow::onActiveViewChange()
|
void MainWindow::onActiveViewChange()
|
||||||
{
|
{
|
||||||
|
// If we are closing the app, we just ignore all view changes (as
|
||||||
|
// docs will be destroyed and views closed).
|
||||||
|
if (get_app_state() != AppState::kNormal)
|
||||||
|
return;
|
||||||
|
|
||||||
// First we have to configure the MainWindow layout (e.g. show
|
// First we have to configure the MainWindow layout (e.g. show
|
||||||
// Timeline if needed) as UIContext::setActiveView() will configure
|
// Timeline if needed) as UIContext::setActiveView() will configure
|
||||||
// several widgets (calling updateUsingEditor() functions) using the
|
// several widgets (calling updateUsingEditor() functions) using the
|
||||||
|
@ -771,4 +784,13 @@ void MainWindow::saveColorBarConfiguration()
|
||||||
m_colorBar->bounds().w);
|
m_colorBar->bounds().w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::saveActiveLayout()
|
||||||
|
{
|
||||||
|
ASSERT(m_layoutSelector);
|
||||||
|
|
||||||
|
auto id = m_layoutSelector->activeLayoutId();
|
||||||
|
auto layout = Layout::MakeFromDock(id, id, m_customizableDock);
|
||||||
|
m_layoutSelector->updateActiveLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|
|
@ -93,7 +93,7 @@ public:
|
||||||
void popTimeline();
|
void popTimeline();
|
||||||
|
|
||||||
void setDefaultLayout();
|
void setDefaultLayout();
|
||||||
void setDefaultMirrorLayout();
|
void setMirroredDefaultLayout();
|
||||||
void loadUserLayout(const Layout* layout);
|
void loadUserLayout(const Layout* layout);
|
||||||
const Dock* customizableDock() const { return m_customizableDock; }
|
const Dock* customizableDock() const { return m_customizableDock; }
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ private:
|
||||||
void configureWorkspaceLayout();
|
void configureWorkspaceLayout();
|
||||||
void saveTimelineConfiguration();
|
void saveTimelineConfiguration();
|
||||||
void saveColorBarConfiguration();
|
void saveColorBarConfiguration();
|
||||||
|
void saveActiveLayout();
|
||||||
|
|
||||||
ui::TooltipManager* m_tooltipManager;
|
ui::TooltipManager* m_tooltipManager;
|
||||||
Dock* m_dock;
|
Dock* m_dock;
|
||||||
|
@ -161,6 +162,7 @@ private:
|
||||||
#endif
|
#endif
|
||||||
obs::scoped_connection m_timelineResizeConn;
|
obs::scoped_connection m_timelineResizeConn;
|
||||||
obs::scoped_connection m_colorBarResizeConn;
|
obs::scoped_connection m_colorBarResizeConn;
|
||||||
|
obs::scoped_connection m_saveDockLayoutConn;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|
Loading…
Reference in New Issue