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;