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 <item> 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.
This commit is contained in:
David Capello 2023-03-21 17:18:05 -03:00
parent 0dbcdc8159
commit 30a88c8e3d
16 changed files with 335 additions and 117 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Aseprite -->
<!-- Copyright (C) 2018-2022 Igara Studio S.A. -->
<!-- Copyright (C) 2018-2023 Igara Studio S.A. -->
<!-- Copyright (C) 2001-2018 David Capello -->
<gui>
<!-- Keyboard shortcuts -->
@ -911,6 +911,23 @@
</item>
<item command="RemoveFrame" text="@.frame_delete_frame" group="cel_delete" />
<separator />
<menu text="@.frame_playback" id="animation_menu" group="cel_animation">
<item command="PlayAnimation" />
<item command="PlayPreviewAnimation" />
<separator />
<item command="SetPlaybackSpeed"><param name="multiplier" value="0.25" /></item>
<item command="SetPlaybackSpeed"><param name="multiplier" value="0.5" /></item>
<item command="SetPlaybackSpeed"><param name="multiplier" value="1.0" /></item>
<item command="SetPlaybackSpeed"><param name="multiplier" value="1.5" /></item>
<item command="SetPlaybackSpeed"><param name="multiplier" value="2.0" /></item>
<item command="SetPlaybackSpeed"><param name="multiplier" value="3.0" /></item>
<separator />
<item command="TogglePlayOnce" />
<item command="TogglePlayAll" />
<item command="TogglePlaySubtags" />
<separator />
<item command="ToggleRewindOnStop" />
</menu>
<menu text="@.frame_tags">
<item command="FrameTagProperties" text="@.frame_tags_tag_properties" />
<separator />
@ -928,7 +945,6 @@
<separator />
<item command="GotoFrame" text="@.frame_go_to_frame" />
</menu>
<item command="PlayAnimation" text="@.frame_play_animation" group="cel_animation" />
<separator />
<item command="FrameProperties" text="@.frame_constant_frame_rate">
<param name="frame" value="all" />

View File

@ -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

View File

@ -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

View File

@ -608,6 +608,16 @@ bool AppMenus::rebuildRecentList()
return true;
}
Menu* AppMenus::getAnimationMenu()
{
auto menuItem =
dynamic_cast<MenuItem*>(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;

View File

@ -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);

View File

@ -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())

View File

@ -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)

View File

@ -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<double> multiplier { this, 1.0, "multiplier" };
};
class SetPlaybackSpeedCommand : public CommandWithNewParams<SetPlaybackSpeedParams> {
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<UIContext*>(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<UIContext*>(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

View File

@ -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<bool>* general,
Option<bool>* preview);
protected:
DocView* docView(Context* ctx) {
if (ctx->isUIAvailable())
return static_cast<UIContext*>(ctx)->activeView();
else
return nullptr;
}
bool onEnabled(Context* ctx) override;
bool onChecked(Context* ctx) override;
void onExecute(Context* ctx) override;
Option<bool>* m_general;
Option<bool>* m_preview;
};
TogglePlayOptionCommand::TogglePlayOptionCommand(
const char* id,
Option<bool>* general,
Option<bool>* 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

View File

@ -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<bool>& playOnce,
Option<bool>& playAll,
Option<bool>& 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());
}
}
}

View File

@ -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<bool>& playOnce,
Option<bool>& playAll,
Option<bool>& playSubtags,
const bool withStopBehaviorOptions);
void showAnimationSpeedMultiplierPopup();
double getAnimationSpeedMultiplier() const;
void setAnimationSpeedMultiplier(double speed);

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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<Doc*> 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;

View File

@ -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<MenuItem*> 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;