From 30a88c8e3d5e2c330a0ab969e36bab839aaba9ba Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 21 Mar 2023 17:18:05 -0300 Subject: [PATCH] Put playback options into Frame > Playback submenu This menu was accessible right-clicking the Play button in the Timeline (and in the Preview window, with specific options for the Preview). This change includes some changes: 1. Now if a menu in gui.xml doesn't specify a text field, the text of the command is used (to avoid double translation, the bad side is that we don't have a mnemonic specified). 2. Menu::showPopup() can be used with submenus from the root menu, to do this we have to remove the menu item owner temporarily before we show the menu as popup (see the change in Menu::showPopup()) 3. We can specify a special active DocView for commands with UIContext::SetTargetView, this is used to set the Preview editor as active view for commands like TogglePlayOnce, etc. --- data/gui.xml | 20 +++- data/strings/en.ini | 16 ++-- src/app/CMakeLists.txt | 2 + src/app/app_menus.cpp | 27 +++++- src/app/app_menus.h | 1 + src/app/commands/cmd_play_animation.cpp | 42 ++++++--- src/app/commands/commands_list.h | 5 + src/app/commands/set_playback_speed.cpp | 66 ++++++++++++++ src/app/commands/toggle_play_option.cpp | 116 ++++++++++++++++++++++++ src/app/ui/editor/editor.cpp | 85 ++++------------- src/app/ui/editor/editor.h | 7 +- src/app/ui/preview_editor.cpp | 8 +- src/app/ui/timeline/ani_controls.cpp | 7 +- src/app/ui_context.cpp | 9 +- src/app/ui_context.h | 27 +++++- src/ui/menu.cpp | 14 ++- 16 files changed, 335 insertions(+), 117 deletions(-) create mode 100644 src/app/commands/set_playback_speed.cpp create mode 100644 src/app/commands/toggle_play_option.cpp diff --git a/data/gui.xml b/data/gui.xml index f4f247f65..ca3373517 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -1,6 +1,6 @@ - + @@ -911,6 +911,23 @@ + + + + + + + + + + + + + + + + + @@ -928,7 +945,6 @@ - diff --git a/data/strings/en.ini b/data/strings/en.ini index 9e1d54e2c..ff3fa987e 100644 --- a/data/strings/en.ini +++ b/data/strings/en.ini @@ -549,6 +549,7 @@ SetInkType = Set Ink Type: {0} SetLoopSection = Set Loop Section SetPalette = Set Palette SetPaletteEntrySize = Set Palette Entry Size +SetPlaybackSpeed = Playback Speed {0}x SetSameInk = Same Ink in All Tools ShowAutoGuides = Show Auto Guides ShowBrushPreview = Show Brush Preview @@ -577,13 +578,17 @@ SwitchNonactiveLayersOpacity = Switch Nonactive Layers Opacity SymmetryMode = Symmetry Mode TiledMode = Tiled Mode Timeline = Switch Timeline +TogglePlayAll = Play All Frames (Ignore Tags) +TogglePlayOnce = Play Once +TogglePlaySubtags = Play Subtags & Repetitions TogglePreview = Toggle Preview +ToggleRewindOnStop = Rewind on Stop ToggleTilesMode = Toggle Tiles Mode +ToggleTimelineThumbnails = Toggle Timeline Thumbnails TilesetMode = Tileset Mode: {} TilesetMode_Manual = Manual TilesetMode_Auto = Auto TilesetMode_Stack = Stack -ToggleTimelineThumbnails = Toggle Timeline Thumbnails Undo = Undo UndoHistory = Undo History UnlinkCel = Unlink Cel @@ -1244,13 +1249,14 @@ layer_merge_down = &Merge Down layer_flatten = &Flatten layer_flatten_visible = Flatten Vi&sible frame = F&rame -frame_properties = Frame &Properties... +frame_properties = &Frame Properties... frame_cel_properties = &Cel Properties... frame_new_frame = &New Frame frame_new_empty_frame = New &Empty Frame frame_duplicate_cels = &Duplicate Cel(s) frame_duplicate_linked_cels = Duplicate &Linked Cel(s) frame_delete_frame = Delete F&rame +frame_playback = &Playback frame_tags = &Tags frame_tags_tag_properties = Tag &Properties... frame_tags_new_tag = New &Tag @@ -1263,7 +1269,6 @@ frame_jump_to_last_frame = &Last Frame frame_jump_to_first_frame_in_tag = First Frame In Tag frame_jump_to_last_frame_in_tag = Last Frame In Tag frame_go_to_frame = &Go to Frame -frame_play_animation = &Play Animation frame_constant_frame_rate = Constant Frame Rate frame_reverse_frames = Re&verse Frames select = Selec&t @@ -1799,11 +1804,6 @@ antialias_tooltip = Smooth font edges [preview] title = Preview -speed_x = Speed x{} -play_once = Play Once -play_all_no_tags = Play All Frames (Ignore Tags) -play_subtags_and_repeats = Play Subtags & Repetitions -rewind_on_stop = Rewind on Stop [recover_files] title = Recover Files diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 3e667460c..178b8682c 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -323,8 +323,10 @@ if(ENABLE_UI) commands/filters/filter_target_buttons.cpp commands/filters/filter_window.cpp commands/screenshot.cpp + commands/set_playback_speed.cpp commands/show_menu.cpp commands/tileset_mode.cpp + commands/toggle_play_option.cpp file_selector.cpp modules/gfx.cpp modules/gui.cpp diff --git a/src/app/app_menus.cpp b/src/app/app_menus.cpp index 1b830942f..004e7f951 100644 --- a/src/app/app_menus.cpp +++ b/src/app/app_menus.cpp @@ -608,6 +608,16 @@ bool AppMenus::rebuildRecentList() return true; } +Menu* AppMenus::getAnimationMenu() +{ + auto menuItem = + dynamic_cast(m_rootMenu->findItemById("animation_menu")); + if (menuItem) + return menuItem->getSubmenu(); + else + return nullptr; +} + void AppMenus::addMenuGroup(const std::string& groupId, MenuItem* menuItem) { @@ -766,10 +776,10 @@ Widget* AppMenus::convertXmlelemToMenuitem(TiXmlElement* elem, Menu* menu) return item; } - const char* command_id = elem->Attribute("command"); + const char* commandId = elem->Attribute("command"); Command* command = - command_id ? Commands::instance()->byId(command_id): - nullptr; + (commandId ? Commands::instance()->byId(commandId): + nullptr); // load params Params params; @@ -793,8 +803,17 @@ Widget* AppMenus::convertXmlelemToMenuitem(TiXmlElement* elem, Menu* menu) if (!menuitem) return nullptr; + // Get menu item text from command friendly name + if (command && menuitem->text().empty()) { + command->loadParams(params); + menuitem->setText(command->friendlyName()); + } + // If the menu item has a specific text, process its mnemonic + else { + menuitem->processMnemonicFromText(); + } + if (id) menuitem->setId(id); - menuitem->processMnemonicFromText(); if (group) { m_groups[group].menu = menu; m_groups[group].end = menuitem; diff --git a/src/app/app_menus.h b/src/app/app_menus.h index 1f7aa1052..644b9fbd8 100644 --- a/src/app/app_menus.h +++ b/src/app/app_menus.h @@ -54,6 +54,7 @@ namespace app { Menu* getSlicePopupMenu() { return m_slicePopupMenu.get(); } Menu* getPalettePopupMenu() { return m_palettePopupMenu.get(); } Menu* getInkPopupMenu() { return m_inkPopupMenu.get(); } + Menu* getAnimationMenu(); void applyShortcutToMenuitemsWithCommand(Command* command, const Params& params, const KeyPtr& key); diff --git a/src/app/commands/cmd_play_animation.cpp b/src/app/commands/cmd_play_animation.cpp index 2b11c3be3..8c499a204 100644 --- a/src/app/commands/cmd_play_animation.cpp +++ b/src/app/commands/cmd_play_animation.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2022 Igara Studio S.A. +// Copyright (C) 2022-2023 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -29,8 +29,9 @@ public: PlayAnimationCommand(); protected: - bool onEnabled(Context* context) override; - void onExecute(Context* context) override; + bool onEnabled(Context* ctx) override; + bool onChecked(Context* ctx) override; + void onExecute(Context* ctx) override; }; PlayAnimationCommand::PlayAnimationCommand() @@ -38,17 +39,23 @@ PlayAnimationCommand::PlayAnimationCommand() { } -bool PlayAnimationCommand::onEnabled(Context* context) +bool PlayAnimationCommand::onEnabled(Context* ctx) { - return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | - ContextFlags::HasActiveSprite); + return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable | + ContextFlags::HasActiveSprite); } -void PlayAnimationCommand::onExecute(Context* context) +bool PlayAnimationCommand::onChecked(Context* ctx) +{ + auto editor = Editor::activeEditor(); + return (editor && editor->isPlaying()); +} + +void PlayAnimationCommand::onExecute(Context* ctx) { // Do not play one-frame images { - ContextReader writer(context); + ContextReader writer(ctx); Sprite* sprite(writer.sprite()); if (!sprite || sprite->totalFrames() < 2) return; @@ -74,8 +81,9 @@ public: PlayPreviewAnimationCommand(); protected: - bool onEnabled(Context* context) override; - void onExecute(Context* context) override; + bool onEnabled(Context* ctx) override; + bool onChecked(Context* ctx) override; + void onExecute(Context* ctx) override; }; PlayPreviewAnimationCommand::PlayPreviewAnimationCommand() @@ -83,13 +91,19 @@ PlayPreviewAnimationCommand::PlayPreviewAnimationCommand() { } -bool PlayPreviewAnimationCommand::onEnabled(Context* context) +bool PlayPreviewAnimationCommand::onEnabled(Context* ctx) { - return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | - ContextFlags::HasActiveSprite); + return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable | + ContextFlags::HasActiveSprite); } -void PlayPreviewAnimationCommand::onExecute(Context* context) +bool PlayPreviewAnimationCommand::onChecked(Context* ctx) +{ + PreviewEditorWindow* preview = App::instance()->mainWindow()->getPreviewEditor(); + return (preview && preview->previewEditor()->isPlaying()); +} + +void PlayPreviewAnimationCommand::onExecute(Context* ctx) { PreviewEditorWindow* preview = App::instance()->mainWindow()->getPreviewEditor(); if (!preview->isPreviewEnabled()) diff --git a/src/app/commands/commands_list.h b/src/app/commands/commands_list.h index 2203b9d57..c6a99c7ae 100644 --- a/src/app/commands/commands_list.h +++ b/src/app/commands/commands_list.h @@ -146,6 +146,7 @@ FOR_EACH_COMMAND(SetInkType) FOR_EACH_COMMAND(SetLoopSection) FOR_EACH_COMMAND(SetPalette) FOR_EACH_COMMAND(SetPaletteEntrySize) +FOR_EACH_COMMAND(SetPlaybackSpeed) FOR_EACH_COMMAND(SetSameInk) FOR_EACH_COMMAND(ShowAutoGuides) FOR_EACH_COMMAND(ShowBrushPreview) @@ -167,7 +168,11 @@ FOR_EACH_COMMAND(SymmetryMode) FOR_EACH_COMMAND(TiledMode) FOR_EACH_COMMAND(TilesetMode) FOR_EACH_COMMAND(Timeline) +FOR_EACH_COMMAND(TogglePlayAll) +FOR_EACH_COMMAND(TogglePlayOnce) +FOR_EACH_COMMAND(TogglePlaySubtags) FOR_EACH_COMMAND(TogglePreview) +FOR_EACH_COMMAND(ToggleRewindOnStop) FOR_EACH_COMMAND(ToggleTilesMode) FOR_EACH_COMMAND(ToggleTimelineThumbnails) FOR_EACH_COMMAND(UndoHistory) diff --git a/src/app/commands/set_playback_speed.cpp b/src/app/commands/set_playback_speed.cpp new file mode 100644 index 000000000..2a67f66fb --- /dev/null +++ b/src/app/commands/set_playback_speed.cpp @@ -0,0 +1,66 @@ +// Aseprite +// Copyright (c) 2023 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/commands/new_params.h" +#include "app/ui_context.h" +#include "app/ui/editor/editor.h" +#include "fmt/format.h" + +namespace app { + +struct SetPlaybackSpeedParams : public NewParams { + Param multiplier { this, 1.0, "multiplier" }; +}; + +class SetPlaybackSpeedCommand : public CommandWithNewParams { +public: + SetPlaybackSpeedCommand(); +protected: + bool onChecked(Context* ctx) override; + void onExecute(Context* ctx) override; + std::string onGetFriendlyName() const override; +}; + +SetPlaybackSpeedCommand::SetPlaybackSpeedCommand() + : CommandWithNewParams(CommandId::SetPlaybackSpeed(), CmdUIOnlyFlag) +{ +} + +bool SetPlaybackSpeedCommand::onChecked(Context* ctx) +{ + Editor* editor = nullptr; + if (ctx->isUIAvailable()) + editor = static_cast(ctx)->activeEditor(); + if (editor) + return (params().multiplier() == editor->getAnimationSpeedMultiplier()); + else + return false; +} + +void SetPlaybackSpeedCommand::onExecute(Context* ctx) +{ + Editor* editor = nullptr; + if (ctx->isUIAvailable()) + editor = static_cast(ctx)->activeEditor(); + if (editor) + editor->setAnimationSpeedMultiplier(params().multiplier()); +} + +std::string SetPlaybackSpeedCommand::onGetFriendlyName() const +{ + return fmt::format(getBaseFriendlyName(), params().multiplier()); +} + +Command* CommandFactory::createSetPlaybackSpeedCommand() +{ + return new SetPlaybackSpeedCommand; +} + +} // namespace app diff --git a/src/app/commands/toggle_play_option.cpp b/src/app/commands/toggle_play_option.cpp new file mode 100644 index 000000000..93ea4ecc8 --- /dev/null +++ b/src/app/commands/toggle_play_option.cpp @@ -0,0 +1,116 @@ +// Aseprite +// Copyright (c) 2023 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/commands/command.h" +#include "app/pref/preferences.h" +#include "app/ui/doc_view.h" +#include "app/ui_context.h" + +namespace app { + +class TogglePlayOptionCommand : public Command { +public: + TogglePlayOptionCommand(const char* id, + Option* general, + Option* preview); + +protected: + DocView* docView(Context* ctx) { + if (ctx->isUIAvailable()) + return static_cast(ctx)->activeView(); + else + return nullptr; + } + + bool onEnabled(Context* ctx) override; + bool onChecked(Context* ctx) override; + void onExecute(Context* ctx) override; + + Option* m_general; + Option* m_preview; +}; + +TogglePlayOptionCommand::TogglePlayOptionCommand( + const char* id, + Option* general, + Option* preview) + : Command(id, CmdUIOnlyFlag) + , m_general(general) + , m_preview(preview) +{ + ASSERT(m_general); +} + +bool TogglePlayOptionCommand::onEnabled(Context* ctx) +{ + if (auto docView = this->docView(ctx)) + return (!docView->isPreview() || m_preview); + else + return false; +} + +bool TogglePlayOptionCommand::onChecked(Context* ctx) +{ + if (auto docView = this->docView(ctx)) + return (docView->isPreview() && m_preview ? (*m_preview)(): (*m_general)()); + else + return false; +} + +void TogglePlayOptionCommand::onExecute(Context* ctx) +{ + if (auto docView = this->docView(ctx)) { + if (docView->isPreview()) { + ASSERT(m_preview); + if (m_preview) + (*m_preview)(!(*m_preview)()); + } + else + (*m_general)(!(*m_general)()); + } +} + +Command* CommandFactory::createTogglePlayOnceCommand() +{ + auto& pref = Preferences::instance(); + return new TogglePlayOptionCommand( + CommandId::TogglePlayOnce(), + &pref.editor.playOnce, + &pref.preview.playOnce); +} + +Command* CommandFactory::createTogglePlayAllCommand() +{ + auto& pref = Preferences::instance(); + return new TogglePlayOptionCommand( + CommandId::TogglePlayAll(), + &pref.editor.playAll, + &pref.preview.playAll); +} + +Command* CommandFactory::createTogglePlaySubtagsCommand() +{ + auto& pref = Preferences::instance(); + return new TogglePlayOptionCommand( + CommandId::TogglePlaySubtags(), + &pref.editor.playSubtags, + &pref.preview.playSubtags); +} + +Command* CommandFactory::createToggleRewindOnStopCommand() +{ + auto& pref = Preferences::instance(); + return new TogglePlayOptionCommand( + CommandId::ToggleRewindOnStop(), + &pref.general.rewindOnStop, + nullptr); // No option for preview +} + +} // namespace app diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index b2fdda59b..351dde72a 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2022 Igara Studio S.A. +// Copyright (C) 2018-2023 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -12,6 +12,7 @@ #include "app/ui/editor/editor.h" #include "app/app.h" +#include "app/app_menus.h" #include "app/color.h" #include "app/color_picker.h" #include "app/color_utils.h" @@ -34,6 +35,7 @@ #include "app/tools/tool_box.h" #include "app/ui/color_bar.h" #include "app/ui/context_bar.h" +#include "app/ui/doc_view.h" #include "app/ui/editor/drawing_state.h" #include "app/ui/editor/editor_customization_delegate.h" #include "app/ui/editor/editor_decorator.h" @@ -2789,76 +2791,29 @@ bool Editor::isPlaying() const return m_isPlaying; } -void Editor::showAnimationSpeedMultiplierPopup(Option& playOnce, - Option& playAll, - Option& playSubtags, - const bool withStopBehaviorOptions) +void Editor::showAnimationSpeedMultiplierPopup() { - const double options[] = { 0.25, 0.5, 1.0, 1.5, 2.0, 3.0 }; - Menu menu; - - for (double option : options) { - MenuItem* item = new MenuItem(fmt::format(Strings::preview_speed_x(), option)); - item->Click.connect([this, option]{ setAnimationSpeedMultiplier(option); }); - item->setSelected(m_aniSpeed == option); - menu.addChild(item); + if (auto menu = AppMenus::instance()->getAnimationMenu()) { + UIContext::SetTargetView setView(m_docView); + menu->showPopup(mousePosInDisplay(), display()); } - menu.addChild(new MenuSeparator); - - // Play once option - { - MenuItem* item = new MenuItem(Strings::preview_play_once()); - item->Click.connect( - [&playOnce]() { - playOnce(!playOnce()); - }); - item->setSelected(playOnce()); - menu.addChild(item); - } - - // Play all option - { - MenuItem* item = new MenuItem(Strings::preview_play_all_no_tags()); - item->Click.connect( - [&playAll]() { - playAll(!playAll()); - }); - item->setSelected(playAll()); - menu.addChild(item); - } - - // Play subtags & repeats - { - MenuItem* item = new MenuItem(Strings::preview_play_subtags_and_repeats()); - item->Click.connect( - [&playSubtags]() { - playSubtags(!playSubtags()); - }); - item->setSelected(playSubtags()); - menu.addChild(item); - } - - if (withStopBehaviorOptions) { - MenuItem* item = new MenuItem(Strings::preview_rewind_on_stop()); - item->Click.connect( - []() { - // Switch the "rewind_on_stop" option - Preferences::instance().general.rewindOnStop( - !Preferences::instance().general.rewindOnStop()); - }); - item->setSelected(Preferences::instance().general.rewindOnStop()); - menu.addChild(item); - } - - menu.showPopup(mousePosInDisplay(), display()); - if (isPlaying()) { // Re-play stop(); - play(playOnce(), - playAll(), - playSubtags()); + + // TODO improve/generalize this in some way + auto& pref = Preferences::instance(); + if (m_docView->isPreview()) { + play(pref.preview.playOnce(), + pref.preview.playAll(), + pref.preview.playSubtags()); + } + else { + play(pref.editor.playOnce(), + pref.editor.playAll(), + pref.editor.playSubtags()); + } } } diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index 27cefbf70..ea34c4818 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2022 Igara Studio S.A. +// Copyright (C) 2018-2023 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -282,10 +282,7 @@ namespace app { bool isPlaying() const; // Shows a popup menu to change the editor animation speed. - void showAnimationSpeedMultiplierPopup(Option& playOnce, - Option& playAll, - Option& playSubtags, - const bool withStopBehaviorOptions); + void showAnimationSpeedMultiplierPopup(); double getAnimationSpeedMultiplier() const; void setAnimationSpeedMultiplier(double speed); diff --git a/src/app/ui/preview_editor.cpp b/src/app/ui/preview_editor.cpp index d21609719..9c9c28f15 100644 --- a/src/app/ui/preview_editor.cpp +++ b/src/app/ui/preview_editor.cpp @@ -332,13 +332,7 @@ void PreviewEditorWindow::onPopupSpeed() if (!miniEditor || !miniEditor->document()) return; - auto& pref = Preferences::instance(); - - miniEditor->showAnimationSpeedMultiplierPopup( - pref.preview.playOnce, - pref.preview.playAll, - pref.preview.playSubtags, - false); + miniEditor->showAnimationSpeedMultiplierPopup(); m_aniSpeed = miniEditor->getAnimationSpeedMultiplier(); } diff --git a/src/app/ui/timeline/ani_controls.cpp b/src/app/ui/timeline/ani_controls.cpp index e34ef872b..4ee51431d 100644 --- a/src/app/ui/timeline/ani_controls.cpp +++ b/src/app/ui/timeline/ani_controls.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2022 Igara Studio S.A. +// Copyright (C) 2018-2023 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -95,10 +95,7 @@ void AniControls::onRightClick(Item* item) auto editor = Editor::activeEditor(); if (item == getItem(ACTION_PLAY) && editor) { - editor->showAnimationSpeedMultiplierPopup( - Preferences::instance().editor.playOnce, - Preferences::instance().editor.playAll, - Preferences::instance().editor.playSubtags, true); + editor->showAnimationSpeedMultiplierPopup(); } } diff --git a/src/app/ui_context.cpp b/src/app/ui_context.cpp index b0500c203..018ce8259 100644 --- a/src/app/ui_context.cpp +++ b/src/app/ui_context.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2019-2022 Igara Studio S.A. +// Copyright (C) 2019-2023 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -34,8 +34,7 @@ namespace app { UIContext* UIContext::m_instance = nullptr; UIContext::UIContext() - : m_lastSelectedView(nullptr) - , m_closedDocs(preferences()) + : m_closedDocs(preferences()) { ASSERT(m_instance == nullptr); m_instance = this; @@ -67,6 +66,10 @@ DocView* UIContext::activeView() const if (!isUIAvailable()) return nullptr; + // Bypass the active workspace view. + if (m_targetView) + return m_targetView; + Workspace* workspace = App::instance()->workspace(); if (!workspace) return nullptr; diff --git a/src/app/ui_context.h b/src/app/ui_context.h index cbdf6c6f7..979fff8bf 100644 --- a/src/app/ui_context.h +++ b/src/app/ui_context.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2019-2021 Igara Studio S.A. +// Copyright (C) 2019-2023 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -49,6 +49,23 @@ namespace app { void reopenLastClosedDoc(); std::vector getAndRemoveAllClosedDocs(); + // Sets the DocView used to run some specific commands + // (e.g. commands that depend on the current view or the preview + // view). + class SetTargetView { + DocView* m_old; + public: + SetTargetView(DocView* newView) { + auto ctx = UIContext::instance(); + m_old = ctx->m_targetView; + ctx->m_targetView = newView; + } + ~SetTargetView() { + auto ctx = UIContext::instance(); + ctx->m_targetView = m_old; + } + }; + protected: void onAddDocument(Doc* doc) override; void onRemoveDocument(Doc* doc) override; @@ -62,7 +79,13 @@ namespace app { void onCloseDocument(Doc* doc) override; private: - DocView* m_lastSelectedView; + DocView* m_lastSelectedView = nullptr; + + // Temporary DocView used to change activeView() behavior, used in + // some specific commands that depends on the current DocView (or + // the preview DocView). + DocView* m_targetView = nullptr; + ClosedDocs m_closedDocs; static UIContext* m_instance; diff --git a/src/ui/menu.cpp b/src/ui/menu.cpp index 98487818a..7eca6b6d7 100644 --- a/src/ui/menu.cpp +++ b/src/ui/menu.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2018-2022 Igara Studio S.A. +// Copyright (C) 2018-2023 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -11,6 +11,7 @@ #include "ui/menu.h" +#include "base/scoped_value.h" #include "gfx/size.h" #include "os/font.h" #include "ui/display.h" @@ -344,6 +345,11 @@ bool MenuItem::hasSubmenu() const void Menu::showPopup(const gfx::Point& pos, Display* parentDisplay) { + // Set the owner menu item to nullptr temporarily in case that we + // are re-using a menu from the root menu as popup menu (e.g. like + // "animation_menu", that is used when right-cliking a Play button) + base::ScopedValue restoreOwner(m_menuitem, nullptr, m_menuitem); + // Generally, when we call showPopup() the menu shouldn't contain a // parent menu-box, because we're filtering kMouseDownMessage to // close the popup automatically when we click outside the menubox. @@ -1169,7 +1175,11 @@ void Menu::highlightItem(MenuItem* menuitem, bool click, bool open_submenu, bool menuitem->invalidate(); // Scroll - View* view = View::getView(menuitem->parent()->parent()); + View* view = nullptr; + if (menuitem->parent() && + menuitem->parent()->parent()) { + view = View::getView(menuitem->parent()->parent()); + } if (view) { gfx::Rect itemBounds = menuitem->bounds(); itemBounds.y -= menuitem->parent()->origin().y;