Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
status-bar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Author:
4 * Tavmjong Bah
5 * Others
6 *
7 * Copyright (C) 2023 Authors
8 *
9 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
10 */
11
12#include "status-bar.h"
13
14#include <iostream>
15#include <string>
16#include <utility>
17#include <vector>
18#include <gtkmm/applicationwindow.h>
19#include <gtkmm/grid.h>
20#include <gtkmm/label.h>
21#include <gtkmm/popover.h>
22#include <gtkmm/popovermenu.h>
23#include <2geom/point.h>
24#include <2geom/angle.h> // deg_from_rad
25
26#include "desktop.h"
27#include "ui/builder-utils.h"
28#include "ui/util.h"
29#include "ui/widget/canvas.h"
35
36namespace Inkscape::UI::Widget {
37
39 : Gtk::Box(Gtk::Orientation::HORIZONTAL)
40{
41 auto builder = Inkscape::UI::create_builder("statusbar.ui");
42
43 auto &statusbar = UI::get_widget<Gtk::Box>(builder, "statusbar");
44
45 // selected_style = &UI::get_widget_derived<UI::Widget::SpinButton>(builder, "statusbar-selected-style");
46 // layer_selector = &UI::get_widget_derived<UI::Widget::SpinButton>(builder, "statusbar-layer-selector");
47 selection = &UI::get_widget<Gtk::Label>(builder, "statusbar-selection");
48
49 // **** Coordinates ****
50
51 coordinates = &UI::get_widget<Gtk::Label> (builder, "statusbar-coordinates");
52
53 // ******** Zoom ********
54
55 zoom = &UI::get_widget<Gtk::Box>(builder, "statusbar-zoom");
56 zoom_value = &UI::get_derived_widget<UI::Widget::SpinButton>(builder, "statusbar-zoom-value");
57
58 // Can't seem to add actions with double parameters to .ui file, add here.
59 const std::vector<std::pair<std::string, std::string>> zoom_entries =
60 {
61 { "10%", "win.canvas-zoom-absolute(0.1)" },
62 { "20%", "win.canvas-zoom-absolute(0.2)" },
63 { "50%", "win.canvas-zoom-absolute(0.5)" },
64 { "100%", "win.canvas-zoom-absolute(1.0)" }, // Must include decimal point!
65 { "200%", "win.canvas-zoom-absolute(2.0)" },
66 { "500%", "win.canvas-zoom-absolute(5.0)" },
67 { "1000%", "win.canvas-zoom-absolute(10.0)"},
68 };
69
70 auto zoom_menu = UI::get_object<Gio::Menu>(builder, "statusbar-zoom-menu");
71 for (auto entry : zoom_entries) {
72 auto menu_item = Gio::MenuItem::create(entry.first, entry.second);
73 zoom_menu->prepend_item(menu_item); // In reverse order.
74 }
75
76 zoom_popover = std::make_unique<Gtk::PopoverMenu>(zoom_menu, Gtk::PopoverMenu::Flags::NESTED);
77 zoom_popover->set_parent(*zoom);
78
79 zoom_value->signal_input().connect(sigc::mem_fun(*this, &StatusBar::zoom_input), true);
80 zoom_value->signal_output().connect(sigc::mem_fun(*this, &StatusBar::zoom_output), true);
81 zoom_value->signal_value_changed().connect(sigc::mem_fun(*this, &StatusBar::zoom_value_changed));
82 on_popup_menu(*zoom_value, sigc::mem_fun(*this, &StatusBar::zoom_popup));
84
85 auto zoom_adjustment = zoom_value->get_adjustment();
86 zoom_adjustment->set_lower(log(SP_DESKTOP_ZOOM_MIN)/log(2));
87 zoom_adjustment->set_upper(log(SP_DESKTOP_ZOOM_MAX)/log(2));
88
89 // ******* Rotate *******
90
91 rotate = &UI::get_widget<Gtk::Box>(builder, "statusbar-rotate");
92 rotate_value = &UI::get_derived_widget<UI::Widget::SpinButton>(builder, "statusbar-rotate-value");
93 rotate_value->set_dont_evaluate(true); // ExpressionEvaluator has trouble parsing degree symbol.
94
95 // Can't seem to add actions with double parameters to .ui file, add here.
96 const std::vector<std::pair<std::string, std::string>> rotate_entries =
97 {
98 { "180°", "win.canvas-rotate-absolute-degrees( 180.0)" }, // Must include decimal point!
99 { "135°", "win.canvas-rotate-absolute-degrees( 135.0)" },
100 { "90°", "win.canvas-rotate-absolute-degrees( 90.0)" },
101 { "45°", "win.canvas-rotate-absolute-degrees( 45.0)" },
102 { "0°", "win.canvas-rotate-absolute-degrees( 0.0)" },
103 { "-45°", "win.canvas-rotate-absolute-degrees( -45.0)" },
104 { "-90°", "win.canvas-rotate-absolute-degrees( -90.0)" },
105 { "-135°", "win.canvas-rotate-absolute-degrees(-135.0)" },
106 };
107
108 auto rotate_menu = UI::get_object<Gio::Menu>(builder, "statusbar-rotate-menu");
109 for (auto entry : rotate_entries) {
110 auto menu_item = Gio::MenuItem::create(entry.first, entry.second);
111 rotate_menu->prepend_item(menu_item); // In reverse order.
112 }
113
114 rotate_popover = std::make_unique<Gtk::PopoverMenu>(rotate_menu, Gtk::PopoverMenu::Flags::NESTED);
115 rotate_popover->set_parent(*rotate);
116
117 rotate_value->signal_output().connect(sigc::mem_fun(*this, &StatusBar::rotate_output), true);
118 rotate_value->signal_value_changed().connect(sigc::mem_fun(*this, &StatusBar::rotate_value_changed));
119 on_popup_menu(*rotate_value, sigc::mem_fun(*this, &StatusBar::rotate_popup));
121
122 // Add rest by hand for now.
123
124 // Selected Style
125 selected_style = Gtk::make_managed<Inkscape::UI::Widget::SelectedStyle>();
126 statusbar.prepend(*selected_style);
127
128 // Layer Selector
129 layer_selector = Gtk::make_managed<Inkscape::UI::Widget::LayerSelector>();
130 layer_selector->set_hexpand(false);
131 statusbar.insert_child_after(*layer_selector, *selected_style);
132
133 // Page selector
134 _page_selector = Gtk::make_managed<PageSelector>();
135 _page_selector->set_hexpand(false);
136 statusbar.insert_child_after(*_page_selector, *layer_selector);
137
138 // Selector status
139 append(statusbar);
140
141 preference_observer = Preferences::get()->createObserver("/statusbar/visibility", [this] {
143 });
145}
146
148{
149 desktop = desktop_in;
150
154
155 // A desktop is always "owned" by a desktop widget.
157
158 if (desktop) {
159 update_zoom();
161 }
162}
163
164void
165StatusBar::set_message(const Inkscape::MessageType type, const char* message)
166{
167 Glib::ustring pattern = "%1";
168#ifndef _WIN32
169#if PANGO_VERSION_CHECK(1,50,0)
170 // line height give delays on windows so better unset, also is not necesary label is well placed without
171 pattern = "<span line_height='0.8'>%1</span>";
172#endif
173#endif
174
175 auto const msg = Glib::ustring::compose(pattern, message ? message : "");
176 selection->set_markup(msg);
177 // we don't use Inkscape::MessageType because previous queue_draw is not needed, is called on allocation and is overridden by next message sent anyway
178 // Allow user to view the entire message even if it doesn't fit into label (after removing markup).
179 selection->set_tooltip_text(selection->get_text());
180}
181
182void
184{
185 char * str_total = g_strdup_printf("(%7.2f, %7.2f)", p.x(), p.y());
186 coordinates->set_markup(str_total);
187 g_free(str_total);
188}
189
190void
192{
193 rotate_value->grab_focus();
194}
195
196void
198{
199 zoom_value->grab_focus();
200}
201
203{
204 desktop_widget->get_canvas()->grab_focus();
205}
206
207// ******** Zoom ********
208
209int
210StatusBar::zoom_input(double &new_value)
211{
212 double value = g_strtod(zoom_value->get_text().c_str(), nullptr);
213 new_value = log(value / 100.0) / log(2);
214 return true;
215}
216
217bool
219{
220 double value = floor (10 * (pow (2, zoom_value->get_value()) * 100.0 + 0.05)) / 10;
221
222 char b[64];
223 if (value < 10) {
224 g_snprintf(b, 64, "%4.1f%%", value);
225 } else {
226 g_snprintf(b, 64, "%4.0f%%", value);
227 }
228 zoom_value->set_text(b);
229
230 return true;
231}
232
234{
235 if (_blocker.pending()) {
236 return;
237 }
238 auto guard = _blocker.block();
239
240 double const zoom_factor = std::pow(2, zoom_value->get_value());
241
242 if (auto const window = dynamic_cast<Gtk::ApplicationWindow *>(get_root())) {
243 auto variant = Glib::Variant<double>::create(zoom_factor);
244 window->activate_action("win.canvas-zoom-absolute", variant);
245 } else {
246 std::cerr << "StatusBar::zoom_value_changed(): failed to get window!" << std::endl;
247 }
248}
249
255
257{
258 if (_blocker.pending()) {
259 return;
260 }
261 auto guard = _blocker.block();
262
263 auto prefs = Preferences::get();
264
265 double correction = 1.0;
266 if (prefs->getDouble("/options/zoomcorrection/shown", true)) {
267 correction = prefs->getDouble("/options/zoomcorrection/value", 1.0);
268 }
269
270 zoom_value->set_value(log(desktop->current_zoom() / correction) / log(2));
271}
272
273// ******* Rotate *******
274
275bool
277{
278 auto val = rotate_value->get_value();
279 if (val < -180) val += 360;
280 if (val > 180) val -= 360;
281
282 char b[64];
283 g_snprintf(b, 64, "%7.2f°", val);
284 rotate_value->set_text(b);
285
286 return true;
287}
288
290{
291 if (_blocker.pending()) {
292 return;
293 }
294 auto guard = _blocker.block();
295
296 if (auto const window = dynamic_cast<Gtk::ApplicationWindow *>(get_root())) {
297 auto variant = Glib::Variant<double>::create(rotate_value->get_value());
298 window->activate_action("win.canvas-rotate-absolute-degrees", variant);
299 } else {
300 std::cerr << "StatusBar::rotate_value_changed(): failed to get window!" << std::endl;
301 }
302}
303
309
311{
312 if (_blocker.pending()) {
313 return;
314 }
315 auto guard = _blocker.block();
316
317 rotate_value->set_value(Geom::deg_from_rad(desktop->current_rotation().angle()));
318}
319
320void
322{
323 auto prefs = Inkscape::Preferences::get();
324 Glib::ustring path("/statusbar/visibility/");
325
326 layer_selector->set_visible(prefs->getBool(path + "layer", true));
327 selected_style->set_visible(prefs->getBool(path + "style", true));
328 coordinates->set_visible( prefs->getBool(path + "coordinates", true));
329 rotate->set_visible( prefs->getBool(path + "rotation", true));
330}
331
332} // namespace Inkscape::UI::Widget
333
334/*
335 Local Variables:
336 mode:c++
337 c-file-style:"stroustrup"
338 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
339 indent-tabs-mode:nil
340 fill-column:99
341 End:
342*/
343// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Cartesian point / 2D vector and related operations.
Various trigoniometric helper functions.
Gtk builder utilities.
Inkscape canvas widget.
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Coord angle() const
Definition transforms.h:204
static Preferences * get()
Access the singleton Preferences object.
std::unique_ptr< PreferencesObserver > createObserver(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
Create an observer watching preference 'path' and calling provided function when preference changes.
void setDesktop(SPDesktop *desktop)
void setDesktop(SPDesktop *desktop)
void setDesktop(SPDesktop *desktop)
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
std::unique_ptr< Gtk::Popover > zoom_popover
Definition status-bar.h:88
void set_desktop(SPDesktop *desktop)
void set_coordinate(const Geom::Point &p)
void set_message(const Inkscape::MessageType type, const char *message)
bool zoom_popup(PopupMenuOptionalClick)
UI::Widget::SpinButton * rotate_value
Definition status-bar.h:85
bool rotate_popup(PopupMenuOptionalClick)
UI::Widget::SpinButton * zoom_value
Definition status-bar.h:84
int zoom_input(double &new_value)
std::unique_ptr< Gtk::Popover > rotate_popover
Definition status-bar.h:89
Inkscape::PrefObserver preference_observer
Definition status-bar.h:95
SPDesktopWidget * desktop_widget
Definition status-bar.h:87
scoped_block block()
Inkscape::UI::Widget::Canvas * get_canvas()
To do: update description of desktop.
Definition desktop.h:149
double current_zoom() const
Definition desktop.h:335
SPDesktopWidget * getDesktopWidget() const
Definition desktop.h:192
Geom::Rotate const & current_rotation() const
Definition desktop.h:366
Glib::ustring msg
A class to hold:
Editable view implementation.
constexpr double SP_DESKTOP_ZOOM_MIN
Definition desktop.h:143
constexpr double SP_DESKTOP_ZOOM_MAX
Definition desktop.h:142
auto floor(Geom::Rect const &rect)
Definition geom.h:131
Definition desktop.h:50
Custom widgets.
Definition desktop.h:126
void popup_at_center(Gtk::Popover &popover, Gtk::Widget &widget)
As popup_at() but point to center of widget.
void on_popup_menu(Gtk::Widget &widget, PopupMenuSlot slot)
Connect slot to a widgetʼs key and button events that traditionally trigger a popup menu,...
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
std::optional< PopupMenuClick > PopupMenuOptionalClick
Optional: not present if popup wasnʼt triggered by click.
Definition popup-menu.h:41
static void append(std::vector< T > &target, std::vector< T > &&source)
MessageType
A hint about the meaning of a message; is it an ordinary message, a message advising the user of some...
Definition message.h:25
Piecewise< SBasis > log(Interval in)
Definition pw-funcs.cpp:37
Glib::RefPtr< Gtk::Builder > builder