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:
David Capello 2024-01-11 16:43:46 -03:00
parent 0fecf551df
commit 72b61fc2e7
12 changed files with 204 additions and 58 deletions

View File

@ -157,6 +157,7 @@
<option id="keep_closed_sprite_on_memory" type="bool" default="true" /> <option id="keep_closed_sprite_on_memory" type="bool" default="true" />
<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="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" />

View File

@ -1174,7 +1174,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
@ -1205,9 +1210,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...

View File

@ -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.
@ -363,6 +363,7 @@ bool Dock::onProcessMessage(ui::Message* msg)
case kMouseUpMessage: { case kMouseUpMessage: {
if (hasCapture()) { if (hasCapture()) {
releaseMouse(); releaseMouse();
onUserResizedDock();
} }
break; break;
} }
@ -391,6 +392,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;

View File

@ -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.
@ -60,6 +60,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;
@ -67,6 +68,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);

View File

@ -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.
@ -122,18 +122,22 @@ 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, LayoutPtr Layout::MakeFromDock(const std::string& id,
const std::string& name,
const Dock* dock) 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");
@ -143,6 +147,17 @@ LayoutPtr Layout::MakeFromDock(const std::string& name,
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)

View File

@ -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,16 +21,23 @@ namespace app {
class Layout final { class Layout final {
public: public:
static constexpr const char* kDefault = "_default_";
static constexpr const char* kMirroredDefault = "_mirrored_default_";
static LayoutPtr MakeFromXmlElement(const TiXmlElement* layoutElem); static LayoutPtr MakeFromXmlElement(const TiXmlElement* layoutElem);
static LayoutPtr MakeFromDock(const std::string& name, static LayoutPtr MakeFromDock(const std::string& id,
const std::string& name,
const Dock* dock); 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;
}; };

View File

@ -127,27 +127,36 @@ 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 { std::string getLayoutId() const {
return m_layout.get(); 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) { void setLayout(const LayoutPtr& layout) {
@ -157,29 +166,28 @@ public:
void selectImmediately() { void selectImmediately() {
MainWindow* win = App::instance()->mainWindow(); MainWindow* win = App::instance()->mainWindow();
switch (m_id) { if (m_layout)
case LayoutId::DEFAULT: m_selector->m_activeLayoutId = m_layout->id();
switch (m_option) {
case LayoutOption::DEFAULT:
win->setDefaultLayout(); win->setDefaultLayout();
break; break;
case LayoutId::DEFAULT_MIRROR: case LayoutOption::MIRRORED_DEFAULT:
win->setDefaultMirrorLayout(); win->setMirroredDefaultLayout();
break;
case LayoutId::USER_DEFINED:
ASSERT(m_layout);
if (m_layout)
win->loadUserLayout(m_layout.get());
break;
default:
// Do nothing
break; 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)
@ -190,14 +198,14 @@ public:
name.getEntryWidget()->setMaxTextLength(128); name.getEntryWidget()->setMaxTextLength(128);
name.setFocusMagnet(true); name.setFocusMagnet(true);
name.setValue( name.setValue(
fmt::format("{} ({})", fmt::format(Strings::new_layout_default_name(),
Strings::new_layout_default_name(),
m_selector->m_layouts.size()+1)); 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(), auto layout = Layout::MakeFromDock(name.getValue(),
name.getValue(),
win->customizableDock()); win->customizableDock());
m_selector->addLayout(layout); m_selector->addLayout(layout);
@ -211,7 +219,7 @@ public:
} }
private: private:
LayoutId m_id; LayoutOption m_option;
LayoutSelector* m_selector; LayoutSelector* m_selector;
LayoutPtr m_layout; LayoutPtr m_layout;
}; };
@ -237,6 +245,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);
@ -257,19 +267,28 @@ 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 = auto item = new LayoutItem(this, LayoutItem::USER_DEFINED,
m_comboBox.addItem(
new LayoutItem(this, LayoutItem::USER_DEFINED,
layout->name(), layout->name(),
layout)); layout);
m_comboBox.setSelectedItemIndex(item); m_comboBox.insertItem(
m_comboBox.getItemCount()-1, // Above the "New Layout" item
item);
m_comboBox.setSelectedItem(item);
} }
else { else {
for (auto item : m_comboBox) { for (auto item : m_comboBox) {
@ -285,6 +304,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()) {
@ -336,18 +364,34 @@ 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(
m_comboBox.addItem(new LayoutItem(this, LayoutItem::DEFAULT_MIRROR, "Default / Mirror")); new LayoutItem(
m_comboBox.addItem(new SeparatorInView("Timeline", HORIZONTAL)); this,
LayoutItem::DEFAULT,
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, m_comboBox.addItem(
new LayoutItem(
this, LayoutItem::USER_DEFINED,
layout->name(), layout->name(),
layout)); layout));
} }
m_comboBox.addItem(
new LayoutItem(
this, LayoutItem::NEW_LAYOUT,
Strings::main_window_new_layout(),
nullptr));
} }
m_comboBox.setVisible(true); m_comboBox.setVisible(true);
@ -361,6 +405,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);
} }
@ -381,4 +428,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

View File

@ -47,7 +47,11 @@ namespace app {
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;
@ -59,9 +63,11 @@ namespace app {
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;

View File

@ -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,11 +38,20 @@ 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(), auto it = std::find_if(m_layouts.begin(), m_layouts.end(),
[layout](const LayoutPtr& l){ [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

View File

@ -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 @@ namespace app {
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);

View File

@ -42,6 +42,7 @@
#include "app/ui_context.h" #include "app/ui_context.h"
#include "base/fs.h" #include "base/fs.h"
#include "os/system.h" #include "os/system.h"
#include "ui/app_state.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/splitter.h" #include "ui/splitter.h"
#include "ui/system.h" #include "ui/system.h"
@ -179,7 +180,15 @@ 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();
@ -200,6 +209,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();
@ -412,7 +422,7 @@ void MainWindow::setDefaultLayout()
configureWorkspaceLayout(); configureWorkspaceLayout();
} }
void MainWindow::setDefaultMirrorLayout() void MainWindow::setMirroredDefaultLayout()
{ {
m_timelineResizeConn.disconnect(); m_timelineResizeConn.disconnect();
m_colorBarResizeConn.disconnect(); m_colorBarResizeConn.disconnect();
@ -494,6 +504,11 @@ void MainWindow::onResize(ui::ResizeEvent& ev)
// 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
@ -772,4 +787,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

View File

@ -98,7 +98,7 @@ namespace app {
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; }
@ -138,6 +138,7 @@ namespace app {
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;
@ -163,6 +164,7 @@ namespace app {
#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;
}; };
} }