Improve the layout selector UI

Changes:
* Now we use the "user data" icon as the button to expand the layouts
  combobox
* Added a tooltip to this icon
* Added buttons to configure the Timeline position in the same
  combobox
* Fixed some bugs in Dock using space for hidden widgets
This commit is contained in:
David Capello 2022-08-25 10:14:09 -03:00
parent ec95323856
commit 544f711adc
8 changed files with 88 additions and 21 deletions

View File

@ -1205,6 +1205,9 @@ help_twitter = Twitter
help_enter_license = Enter &License help_enter_license = Enter &License
help_about = &About help_about = &About
[main_window]
layout = User Interface Layout
[mask_by_color] [mask_by_color]
title = Select Color title = Select Color
label_color = Color: label_color = Color:

View File

@ -53,7 +53,8 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
m_box = new app::gen::TimelineConf(); m_box = new app::gen::TimelineConf();
addChild(m_box); addChild(m_box);
m_box->position()->ItemChange.connect([this] { onChangePosition(); }); m_box->position()->ItemChange.connect(
[this] { onChangeTimelinePosition(m_box->position()->selectedItem()); });
m_box->firstFrame()->Change.connect([this] { onChangeFirstFrame(); }); m_box->firstFrame()->Change.connect([this] { onChangeFirstFrame(); });
m_box->merge()->Click.connect([this] { onChangeType(); }); m_box->merge()->Click.connect([this] { onChangeType(); });
m_box->tint()->Click.connect([this] { onChangeType(); }); m_box->tint()->Click.connect([this] { onChangeType(); });
@ -147,12 +148,12 @@ bool ConfigureTimelinePopup::onProcessMessage(ui::Message* msg)
return PopupWindow::onProcessMessage(msg); return PopupWindow::onProcessMessage(msg);
} }
void ConfigureTimelinePopup::onChangePosition() void ConfigureTimelinePopup::onChangeTimelinePosition(int option)
{ {
gen::TimelinePosition newTimelinePos = gen::TimelinePosition::BOTTOM; gen::TimelinePosition newTimelinePos = gen::TimelinePosition::BOTTOM;
int selITem = m_box->position()->selectedItem(); int selItem = option;
switch (selITem) { switch (selItem) {
case 0: newTimelinePos = gen::TimelinePosition::LEFT; break; case 0: newTimelinePos = gen::TimelinePosition::LEFT; break;
case 1: newTimelinePos = gen::TimelinePosition::RIGHT; break; case 1: newTimelinePos = gen::TimelinePosition::RIGHT; break;
case 2: newTimelinePos = gen::TimelinePosition::BOTTOM; break; case 2: newTimelinePos = gen::TimelinePosition::BOTTOM; break;

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2025 Igara Studio S.A. // Copyright (C) 2022-2025 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -31,9 +31,10 @@ class ConfigureTimelinePopup : public ui::PopupWindow {
public: public:
ConfigureTimelinePopup(); ConfigureTimelinePopup();
static void onChangeTimelinePosition(int option);
protected: protected:
bool onProcessMessage(ui::Message* msg) override; bool onProcessMessage(ui::Message* msg) override;
void onChangePosition();
void onChangeFirstFrame(); void onChangeFirstFrame();
void onChangeType(); void onChangeType();
void onOpacity(); void onOpacity();

View File

@ -227,17 +227,16 @@ void Dock::onSizeHint(ui::SizeHintEvent& ev)
{ {
gfx::Size sz = border().size(); gfx::Size sz = border().size();
if (m_sides[kLeftIndex]) if (hasVisibleSide(kLeftIndex))
sz.w += m_sides[kLeftIndex]->sizeHint().w + childSpacing(); sz.w += m_sides[kLeftIndex]->sizeHint().w + childSpacing();
if (m_sides[kRightIndex]) if (hasVisibleSide(kRightIndex))
sz.w += m_sides[kRightIndex]->sizeHint().w + childSpacing(); sz.w += m_sides[kRightIndex]->sizeHint().w + childSpacing();
if (m_sides[kTopIndex]) if (hasVisibleSide(kTopIndex))
sz.h += m_sides[kTopIndex]->sizeHint().h + childSpacing(); sz.h += m_sides[kTopIndex]->sizeHint().h + childSpacing();
if (m_sides[kBottomIndex]) if (hasVisibleSide(kBottomIndex))
sz.h += m_sides[kBottomIndex]->sizeHint().h + childSpacing(); sz.h += m_sides[kBottomIndex]->sizeHint().h + childSpacing();
if (m_sides[kCenterIndex]) { if (hasVisibleSide(kCenterIndex))
sz += m_sides[kCenterIndex]->sizeHint(); sz += m_sides[kCenterIndex]->sizeHint();
}
ev.setSizeHint(sz); ev.setSizeHint(sz);
} }

View File

@ -72,6 +72,8 @@ private:
const gfx::Rect& separator, const gfx::Rect& separator,
const int index)> f); const int index)> f);
bool hasVisibleSide(const int i) const { return (m_sides[i] && m_sides[i]->isVisible()); }
std::array<Widget*, kSides> m_sides; std::array<Widget*, kSides> m_sides;
std::array<int, kSides> m_aligns; std::array<int, kSides> m_aligns;
std::array<gfx::Size, kSides> m_sizes; std::array<gfx::Size, kSides> m_sizes;

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2021 Igara Studio S.A. // Copyright (C) 2021-2022 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.
@ -11,10 +11,14 @@
#include "app/ui/layout_selector.h" #include "app/ui/layout_selector.h"
#include "app/app.h" #include "app/app.h"
#include "app/i18n/strings.h"
#include "app/ui/button_set.h"
#include "app/ui/configure_timeline_popup.h"
#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 "ui/listitem.h" #include "ui/listitem.h"
#include "ui/tooltips.h"
#include "ui/window.h" #include "ui/window.h"
#define ANI_TICKS 5 #define ANI_TICKS 5
@ -49,6 +53,41 @@ private:
LayoutId m_id; LayoutId m_id;
}; };
// TODO Similar ButtonSet to the one in timeline_conf.xml
class TimelineButtons : public ButtonSet {
public:
TimelineButtons() : ButtonSet(2)
{
addItem(Strings::timeline_conf_left())->processMnemonicFromText();
addItem(Strings::timeline_conf_right())->processMnemonicFromText();
addItem(Strings::timeline_conf_bottom(), 2)->processMnemonicFromText();
Preferences::instance().general.timelinePosition.AfterChange.connect(
[this](gen::TimelinePosition position) {
int selItem = 0;
switch (position) {
case gen::TimelinePosition::LEFT: selItem = 0; break;
case gen::TimelinePosition::RIGHT: selItem = 1; break;
case gen::TimelinePosition::BOTTOM: selItem = 2; break;
}
setSelectedItem(selItem, false);
});
InitTheme.connect([this] {
auto theme = skin::SkinTheme::get(this);
setStyle(theme->styles.separatorInView());
});
initTheme();
}
private:
void onItemChange(Item* item) override
{
ButtonSet::onItemChange(item);
ConfigureTimelinePopup::onChangeTimelinePosition(selectedItem());
}
};
}; // namespace }; // namespace
void LayoutSelector::LayoutComboBox::onChange() void LayoutSelector::LayoutComboBox::onChange()
@ -58,7 +97,8 @@ void LayoutSelector::LayoutComboBox::onChange()
} }
} }
LayoutSelector::LayoutSelector() : m_button("", "\xc3\xb7") LayoutSelector::LayoutSelector(TooltipManager* tooltipManager)
: m_button(SkinTheme::instance()->parts.iconUserData())
{ {
m_button.Click.connect([this]() { switchSelector(); }); m_button.Click.connect([this]() { switchSelector(); });
@ -66,6 +106,15 @@ LayoutSelector::LayoutSelector() : m_button("", "\xc3\xb7")
addChild(&m_comboBox); addChild(&m_comboBox);
addChild(&m_button); addChild(&m_button);
setupTooltips(tooltipManager);
InitTheme.connect([this] {
noBorderNoChildSpacing();
m_comboBox.noBorderNoChildSpacing();
m_button.noBorderNoChildSpacing();
});
initTheme();
} }
LayoutSelector::~LayoutSelector() LayoutSelector::~LayoutSelector()
@ -80,8 +129,8 @@ void LayoutSelector::onAnimationFrame()
case ANI_EXPANDING: case ANI_EXPANDING:
case ANI_COLLAPSING: { case ANI_COLLAPSING: {
const double t = animationTime(); const double t = animationTime();
m_comboBox.setSizeHint(gfx::Size((1.0 - t) * m_startSize.w + t * m_endSize.w, m_comboBox.setSizeHint(gfx::Size(int(inbetween(m_startSize.w, m_endSize.w, t)),
(1.0 - t) * m_startSize.h + t * m_endSize.h)); int(inbetween(m_startSize.h, m_endSize.h, t))));
break; break;
} }
} }
@ -112,8 +161,11 @@ 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 LayoutItem(LayoutId::DEFAULT, "Default")); m_comboBox.addItem(new LayoutItem(LayoutId::DEFAULT, "Default"));
m_comboBox.addItem(new LayoutItem(LayoutId::DEFAULT_MIRROR, "Default / Mirror")); m_comboBox.addItem(new LayoutItem(LayoutId::DEFAULT_MIRROR, "Default / Mirror"));
m_comboBox.addItem(new SeparatorInView("Timeline", HORIZONTAL));
m_comboBox.addItem(new TimelineButtons());
} }
m_comboBox.setVisible(true); m_comboBox.setVisible(true);
@ -131,4 +183,9 @@ void LayoutSelector::switchSelector()
startAnimation((expand ? ANI_EXPANDING : ANI_COLLAPSING), ANI_TICKS); startAnimation((expand ? ANI_EXPANDING : ANI_COLLAPSING), ANI_TICKS);
} }
void LayoutSelector::setupTooltips(TooltipManager* tooltipManager)
{
tooltipManager->addTooltipFor(&m_button, Strings::main_window_layout(), TOP);
}
} // namespace app } // namespace app

View File

@ -9,13 +9,17 @@
#pragma once #pragma once
#include "app/ui/dockable.h" #include "app/ui/dockable.h"
#include "app/ui/icon_button.h"
#include "ui/animated_widget.h" #include "ui/animated_widget.h"
#include "ui/box.h" #include "ui/box.h"
#include "ui/combobox.h" #include "ui/combobox.h"
#include "ui/link_label.h"
#include <memory> #include <memory>
namespace ui {
class TooltipManager;
}
namespace app { namespace app {
class LayoutSelector : public ui::HBox, class LayoutSelector : public ui::HBox,
@ -32,19 +36,20 @@ class LayoutSelector : public ui::HBox,
}; };
public: public:
LayoutSelector(); LayoutSelector(ui::TooltipManager* tooltipManager);
~LayoutSelector(); ~LayoutSelector();
// Dockable impl // Dockable impl
int dockableAt() const override { return ui::TOP | ui::BOTTOM; } int dockableAt() const override { return ui::TOP | ui::BOTTOM; }
private: private:
void setupTooltips(ui::TooltipManager* tooltipManager);
void onAnimationFrame() override; void onAnimationFrame() override;
void onAnimationStop(int animation) override; void onAnimationStop(int animation) override;
void switchSelector(); void switchSelector();
LayoutComboBox m_comboBox; LayoutComboBox m_comboBox;
ui::LinkLabel m_button; IconButton m_button;
gfx::Size m_startSize; gfx::Size m_startSize;
gfx::Size m_endSize; gfx::Size m_endSize;
}; };

View File

@ -93,7 +93,6 @@ MainWindow::MainWindow()
, m_tooltipManager(new TooltipManager) , m_tooltipManager(new TooltipManager)
, m_dock(new Dock) , m_dock(new Dock)
, m_customizableDock(new Dock) , m_customizableDock(new Dock)
, m_layoutSelector(new LayoutSelector)
, m_mode(NormalMode) , m_mode(NormalMode)
, m_homeView(nullptr) , m_homeView(nullptr)
, m_scalePanic(nullptr) , m_scalePanic(nullptr)
@ -117,7 +116,7 @@ MainWindow::MainWindow()
void MainWindow::initialize() void MainWindow::initialize()
{ {
m_menuBar = std::make_unique<MainMenuBar>(); m_menuBar = std::make_unique<MainMenuBar>();
m_layoutSelector = std::make_unique<LayoutSelector>(); m_layoutSelector = std::make_unique<LayoutSelector>(m_tooltipManager);
// Register commands to load menus+shortcuts for these commands // Register commands to load menus+shortcuts for these commands
Editor::registerCommands(); Editor::registerCommands();