1// Aseprite
2// Copyright (C) 2021 Igara Studio S.A.
3// Copyright (C) 2001-2017 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/app.h"
13#include "app/commands/command.h"
14#include "app/commands/params.h"
15#include "app/i18n/strings.h"
16#include "app/modules/editors.h"
17#include "app/pref/preferences.h"
18#include "app/ui/editor/editor.h"
19#include "base/convert_to.h"
20#include "fmt/format.h"
21#include "render/zoom.h"
22#include "ui/manager.h"
23#include "ui/system.h"
24
25namespace app {
26
27class ZoomCommand : public Command {
28public:
29 enum class Action { In, Out, Set };
30 enum class Focus { Default, Mouse, Center };
31
32 ZoomCommand();
33
34protected:
35 bool onNeedsParams() const override { return true; }
36 void onLoadParams(const Params& params) override;
37 bool onEnabled(Context* context) override;
38 void onExecute(Context* context) override;
39 std::string onGetFriendlyName() const override;
40
41private:
42 Action m_action;
43 render::Zoom m_zoom;
44 Focus m_focus;
45};
46
47ZoomCommand::ZoomCommand()
48 : Command(CommandId::Zoom(), CmdUIOnlyFlag)
49 , m_action(Action::In)
50 , m_zoom(1, 1)
51 , m_focus(Focus::Default)
52{
53}
54
55void ZoomCommand::onLoadParams(const Params& params)
56{
57 std::string action = params.get("action");
58 if (action == "in") m_action = Action::In;
59 else if (action == "out") m_action = Action::Out;
60 else if (action == "set") m_action = Action::Set;
61
62 std::string percentage = params.get("percentage");
63 if (!percentage.empty()) {
64 m_zoom = render::Zoom::fromScale(
65 std::strtod(percentage.c_str(), NULL) / 100.0);
66 m_action = Action::Set;
67 }
68
69 m_focus = Focus::Default;
70 std::string focus = params.get("focus");
71 if (focus == "center") m_focus = Focus::Center;
72 else if (focus == "mouse") m_focus = Focus::Mouse;
73}
74
75bool ZoomCommand::onEnabled(Context* context)
76{
77 return (current_editor != NULL);
78}
79
80void ZoomCommand::onExecute(Context* context)
81{
82 // Use the current editor by default.
83 Editor* editor = current_editor;
84 gfx::Point mousePos = ui::get_mouse_position();
85
86 // Try to use the editor above the mouse.
87 ui::Widget* pick = ui::Manager::getDefault()->pickFromScreenPos(mousePos);
88 if (pick && pick->type() == Editor::Type())
89 editor = static_cast<Editor*>(pick);
90
91 render::Zoom zoom = editor->zoom();
92
93 switch (m_action) {
94 case Action::In:
95 zoom.in();
96 break;
97 case Action::Out:
98 zoom.out();
99 break;
100 case Action::Set:
101 zoom = m_zoom;
102 break;
103 }
104
105 Focus focus = m_focus;
106 if (focus == Focus::Default) {
107 if (Preferences::instance().editor.zoomFromCenterWithKeys()) {
108 focus = Focus::Center;
109 }
110 else {
111 focus = Focus::Mouse;
112 }
113 }
114
115 editor->setZoomAndCenterInMouse(
116 zoom,
117 editor->display()->nativeWindow()->pointFromScreen(mousePos),
118 (focus == Focus::Center ? Editor::ZoomBehavior::CENTER:
119 Editor::ZoomBehavior::MOUSE));
120}
121
122std::string ZoomCommand::onGetFriendlyName() const
123{
124 std::string text;
125
126 switch (m_action) {
127 case Action::In:
128 text = Strings::commands_Zoom_In();
129 break;
130 case Action::Out:
131 text = Strings::commands_Zoom_Out();
132 break;
133 case Action::Set:
134 text = fmt::format(Strings::commands_Zoom_Set(),
135 int(100.0*m_zoom.scale()));
136 break;
137 }
138
139 return text;
140}
141
142Command* CommandFactory::createZoomCommand()
143{
144 return new ZoomCommand;
145}
146
147} // namespace app
148