Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
dialog-notebook.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors: see git history
7 * Tavmjong Bah
8 * Mike Kowalski
9 *
10 * Copyright (c) 2018 Tavmjong Bah, Authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include <algorithm>
16#include <iostream>
17#include <optional>
18#include <tuple>
19#include <utility>
20#include <glibmm/i18n.h>
21#include <glibmm/value.h>
22#include <gdkmm/contentprovider.h>
23#include <gtkmm/button.h>
24#include <gtkmm/menubutton.h>
25#include <gtkmm/gestureclick.h>
26#include <gtkmm/separator.h>
27#include <gtkmm/eventcontrollerscroll.h>
28#include <gtkmm/grid.h>
29
30#include "dialog-notebook.h"
31
32#include "enums.h"
33#include "inkscape.h"
34#include "inkscape-window.h"
35#include "preferences.h"
42#include "ui/icon-loader.h"
43#include "ui/util.h"
45
46namespace Inkscape::UI::Dialog {
47
48std::list<DialogNotebook *> DialogNotebook::_instances;
49static const Glib::Quark dialog_notebook_id("dialog-notebook-id");
50
52 return dynamic_cast<DialogNotebook*>(static_cast<Gtk::Widget*>(tabs->get_data(dialog_notebook_id)));
53}
54
55Gtk::Widget* find_dialog_page(Widget::TabStrip* tabs, int position) {
56 if (!tabs) return nullptr;
57
58 if (auto notebook = find_dialog_notebook(tabs)) {
59 return notebook->get_page(position);
60 }
61 return nullptr;
62}
63
69DialogNotebook::DialogNotebook(DialogContainer* container) : _container(container) {
70
71 set_name("DialogNotebook");
72 set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::NEVER);
73 set_has_frame(false);
74 set_vexpand(true);
75 set_hexpand(true);
76
77 // =========== Getting preferences ==========
78 auto prefs = Inkscape::Preferences::get();
79
80 _label_pref = prefs->createObserver("/options/notebooklabels/value", [=,this](const auto& entry) {
81 auto status = entry.getInt();
82 auto labels = UI::Widget::TabStrip::Never;
83 if (status == PREFS_NOTEBOOK_LABELS_AUTO) {
85 }
86 else if (status == PREFS_NOTEBOOK_LABELS_ACTIVE) {
88 }
89 _tabs.set_show_labels(labels);
90 });
91 _label_pref->call();
92
93 _tabclose_pref = prefs->createObserver("/options/notebooktabs/show-closebutton", [this](const auto& entry) {
94 _tabs.set_show_close_button(entry.getBool());
95 });
96 _tabclose_pref->call();
97
98 // ============= Notebook menu ==============
99 _notebook.set_name("DockedDialogNotebook");
100 _notebook.set_show_border(false);
101 _notebook.set_group_name("InkscapeDialogGroup");
102 _notebook.set_scrollable(true);
103 _notebook.set_show_tabs(false);
104
105 auto box = dynamic_cast<Gtk::Box*>(_notebook.get_first_child());
106 if (box) {
107 auto scroll_controller = Gtk::EventControllerScroll::create();
108 scroll_controller->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL | Gtk::EventControllerScroll::Flags::DISCRETE);
109 box->add_controller(scroll_controller);
110 scroll_controller->signal_scroll().connect(sigc::mem_fun(*this, &DialogNotebook::on_scroll_event), false);
111 }
112
116
118
119 auto const menubtn = Gtk::make_managed<Gtk::MenuButton>();
120 menubtn->set_icon_name("go-down-symbolic");
121 menubtn->set_has_frame(false);
122 menubtn->set_popover(_menu_dock);
123 menubtn->set_visible(true);
124 menubtn->set_valign(Gtk::Align::CENTER);
125 menubtn->set_halign(Gtk::Align::CENTER);
126 menubtn->set_focusable(false);
127 menubtn->set_can_focus(false);
128 menubtn->set_focus_on_click(false);
129 menubtn->set_name("DialogMenuButton");
130
131 // =============== Signals ==================
132 _conn.emplace_back(_notebook.signal_page_added().connect(sigc::mem_fun(*this, &DialogNotebook::on_page_added)));
133 _conn.emplace_back(_notebook.signal_page_removed().connect(sigc::mem_fun(*this, &DialogNotebook::on_page_removed)));
134 _conn.emplace_back(_notebook.signal_switch_page().connect(sigc::mem_fun(*this, &DialogNotebook::on_page_switch)));
135
136 _tabs.set_hexpand();
138 _tabs.signal_select_tab().connect([this](auto& tab) {
139 _tabs.select_tab(tab);
140 int page_pos = _tabs.get_tab_position(tab);
141 _notebook.set_current_page(page_pos);
142 auto curr_page = _notebook.get_nth_page(page_pos);
143 if (auto dialog = dynamic_cast<DialogBase*>(curr_page)) {
144 dialog->focus_dialog();
145 }
146 });
147 _tabs.signal_close_tab().connect([this](auto& tab) {
148 auto index = _tabs.get_tab_position(tab);
149 if (auto page = _notebook.get_nth_page(index)) {
150 close_tab(page);
151 }
152 });
153 _tabs.signal_float_tab().connect([this](auto& tab) {
154 // make docked dialog float
155 auto index = _tabs.get_tab_position(tab);
156 if (auto page = _notebook.get_nth_page(index)) {
157 float_tab(*page);
158 }
159 });
160 _tabs.signal_move_tab().connect([this](auto& tab, int src_position, auto& source, int dest_position) {
161 // move tab from source tabstrip/notebook here
162 if (auto notebook = dynamic_cast<DialogNotebook*>(static_cast<Gtk::Widget*>(source.get_data(dialog_notebook_id)))) {
163 if (auto page = notebook->get_page(src_position)) {
164 move_tab_from(*notebook, *page, dest_position);
165 }
166 }
167 });
168 _tabs.signal_tab_rearranged().connect([this](int from, int to) {
169 if (auto page = _notebook.get_nth_page(from)) {
170 _notebook.reorder_child(*page, to);
171 }
172 });
173 _tabs.signal_dnd_begin().connect([this] {
175 for (auto instance : _instances) {
176 instance->add_highlight_header();
177 }
178 });
179 _tabs.signal_dnd_end().connect([this](bool) {
180 // Remove dropzone highlights
182 for (auto instance : _instances) {
183 instance->remove_highlight_header();
184 }
185 });
186 // remember tabs owner
187 _tabs.set_data(dialog_notebook_id, this);
188
189 auto hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
190 hbox->append(_tabs);
191 hbox->append(*menubtn);
192 _content.append(*hbox);
193 auto sep = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
194 sep->set_size_request(-1, 1);
195 _content.append(*sep);
196 _content.append(_notebook);
197 set_child(_content);
198
199 _instances.push_back(this);
200}
201
202void DialogNotebook::build_docking_menu(UI::Widget::PopoverMenu& menu) {
203 const auto icon_size = Gtk::IconSize::NORMAL;
204
205 // Docking menu options
206 auto grid = Gtk::make_managed<Gtk::Grid>();
207 grid->set_name("MenuDockingRect");
208 auto make_item = [icon_size](const char* icon, const char* tooltip) {
209 auto item = Gtk::make_managed<UI::Widget::PopoverMenuItem>("", true, icon, icon_size);
210 item->set_tooltip_text(_(tooltip));
211 return item;
212 };
213 auto dock_lt = make_item("dock-left-top", "Dock current tab at the top left");
214 _conn.emplace_back(dock_lt->signal_activate().connect([this]{ dock_current_tab(DialogContainer::TopLeft); }));
215 auto dock_rt = make_item("dock-right-top", "Dock current tab at the top right");
216 _conn.emplace_back(dock_rt->signal_activate().connect([this]{ dock_current_tab(DialogContainer::TopRight); }));
217 auto dock_lb = make_item("dock-left-bottom", "Dock current tab at the bottom left");
218 _conn.emplace_back(dock_lb->signal_activate().connect([this]{ dock_current_tab(DialogContainer::BottomLeft); }));
219 auto dock_rb = make_item("dock-right-bottom", "Dock current tab at the bottom right");
220 _conn.emplace_back(dock_rb->signal_activate().connect([this]{ dock_current_tab(DialogContainer::BottomRight); }));
221 // Move to new window
222 auto floating = make_item("floating-dialog", "Move current tab to new window");
223 floating->set_valign(Gtk::Align::CENTER);
224 _conn.emplace_back(floating->signal_activate().connect([this]{ pop_tab(nullptr); }));
225 grid->attach(*dock_lt, 0, 0);
226 grid->attach(*dock_lb, 0, 1);
227 grid->attach(*floating, 1, 0, 1, 2);
228 grid->attach(*dock_rt, 2, 0);
229 grid->attach(*dock_rb, 2, 1);
230 int row = 0;
231 menu.attach(*grid, 0, 1, row, row + 1);
232 row++;
233 auto sep = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
234 sep->set_size_request(-1, 1);
235 menu.attach(*sep, 0, 1, row, row + 1);
236 row++;
237 // Close tab
238 auto new_menu_item = Gtk::make_managed<UI::Widget::PopoverMenuItem>(_("Close Tab"));
239 _conn.emplace_back(new_menu_item->signal_activate().connect([this]{ close_tab(nullptr); }));
240 menu.attach(*new_menu_item, 0, 1, row, row + 1);
241 row++;
242 // Close notebook
243 new_menu_item = Gtk::make_managed<UI::Widget::PopoverMenuItem>(_("Close Panel"));
244 _conn.emplace_back(new_menu_item->signal_activate().connect([this]{ close_notebook(); }));
245 menu.attach(*new_menu_item, 0, 1, row, row + 1);
246
247 if (Preferences::get()->getBool("/theme/symbolicIcons", true)) {
248 menu.add_css_class("symbolic");
249 }
250}
251
252void DialogNotebook::build_dialog_menu(UI::Widget::PopoverMenu& menu) {
253 // dialogs are already ordered by categories, and that is exactly how they should be arranged in the menu
254 auto dialog_data = get_dialog_data_list();
255 const auto icon_size = Gtk::IconSize::NORMAL;
256 int row = 0;
257 auto builder = ColumnMenuBuilder<DialogData::Category>{menu, 2, icon_size, row};
258 for (auto const &data : dialog_data) {
259 if (data.category == DialogData::Diagnostics) {
260 continue; // hide dev dialogs from dialogs menu
261 }
262 auto callback = [this, key = data.key]{
263 // get desktop's container, it may be different than current '_container'!
264 if (auto desktop = SP_ACTIVE_DESKTOP) {
265 if (auto container = desktop->getContainer()) {
266 // open dialog and dock it here if request comes from the main window and dialog was not floating;
267 // if we are in a floating dialog window, then do not dock new dialog here, it is not useful
268 bool floating = DialogManager::singleton().should_open_floating(key);
269 container->new_dialog(key, container == _container && !floating ? this : nullptr);
270 }
271 }
272 };
273 builder.add_item(data.label, data.category, {}, data.icon_name, true, false, std::move(callback));
274 if (builder.new_section()) {
275 builder.set_section(gettext(dialog_categories[data.category]));
276 }
277 }
278
279 if (Preferences::get()->getBool("/theme/symbolicIcons", true)) {
280 menu.add_css_class("symbolic");
281 }
282}
283
284DialogNotebook::~DialogNotebook()
285{
286 // disconnect signals first, so no handlers are invoked when removing pages
287 _conn.clear();
288 _connmenu.clear();
289
290 // Unlink and remove pages
291 for (int i = _notebook.get_n_pages(); i >= 0; --i) {
292 DialogBase *dialog = dynamic_cast<DialogBase *>(_notebook.get_nth_page(i));
293 _container->unlink_dialog(dialog);
294 _notebook.remove_page(i);
295 }
296
297 _instances.remove(this);
298}
299
300void DialogNotebook::add_highlight_header()
301{
302 _notebook.add_css_class("nb-highlight");
303}
304
305void DialogNotebook::remove_highlight_header()
306{
307 _notebook.remove_css_class("nb-highlight");
308}
309
313bool DialogNotebook::provide_scroll(Gtk::Widget &page) {
314 auto const &dialog_data = get_dialog_data();
315 auto dialogbase = dynamic_cast<DialogBase*>(&page);
316 if (dialogbase) {
317 auto data = dialog_data.find(dialogbase->get_type());
318 if ((*data).second.provide_scroll == ScrollProvider::PROVIDE) {
319 return true;
320 }
321 }
322 return false;
323}
324
325Gtk::ScrolledWindow* DialogNotebook::get_scrolledwindow(Gtk::Widget &page)
326{
327 if (auto const children = UI::get_children(page); !children.empty()) {
328 if (auto const scrolledwindow = dynamic_cast<Gtk::ScrolledWindow *>(children[0])) {
329 return scrolledwindow;
330 }
331 }
332 return nullptr;
333}
334
338Gtk::ScrolledWindow* DialogNotebook::get_current_scrolledwindow(bool const skip_scroll_provider)
339{
340 auto const pagenum = _notebook.get_current_page();
341 if (auto const page = _notebook.get_nth_page(pagenum)) {
342 if (skip_scroll_provider && provide_scroll(*page)) {
343 return nullptr;
344 }
345 return get_scrolledwindow(*page);
346 }
347 return nullptr;
348}
349
353void DialogNotebook::add_page(Gtk::Widget &page) {
354 page.set_vexpand();
355
356 // TODO: It is not exactly great to replace the passed pageʼs children from under it like this.
357 // But stopping it requires to ensure all references to page elsewhere are right/updated.
358 if (auto const box = dynamic_cast<Gtk::Box *>(&page)) {
359 auto const wrapper = Gtk::make_managed<Gtk::ScrolledWindow>();
360 wrapper->set_vexpand(true);
361 wrapper->set_propagate_natural_height(true);
362 wrapper->set_overlay_scrolling(false);
363 wrapper->get_style_context()->add_class("noborder");
364
365 auto const wrapperbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
366 wrapperbox->set_vexpand(true);
367
368 // This used to transfer pack-type and child properties, but now those are set on children.
369 for_each_child(*box, [=](Gtk::Widget &child) {
370 child.reference();
371 box ->remove(child);
372 wrapperbox->append(child);
373 child.unreference();
375 });
376
377 wrapper->set_child(*wrapperbox);
378 box ->append(*wrapper);
379
380 if (provide_scroll(page)) {
381 wrapper->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::EXTERNAL);
382 } else {
383 wrapper->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
384 }
385 }
386
387 add_notebook_page(page, -1);
388}
389
390void DialogNotebook::add_notebook_page(Gtk::Widget& page, int position) {
391 int page_number = _notebook.insert_page(page, position);
392 _notebook.set_tab_reorderable(page);
393 _notebook.set_tab_detachable(page);
394 _notebook.set_current_page(page_number);
395}
396
400void DialogNotebook::move_page(Gtk::Widget &page)
401{
402 // Find old notebook
403 auto old_notebook = get_page_notebook(page);
404 if (!old_notebook) {
405 std::cerr << "DialogNotebook::move_page: page not in notebook!" << std::endl;
406 return;
407 }
408 if (old_notebook == &_notebook) {
409 return; // no op
410 }
411
412 // Keep references until re-attachment
413 page.reference();
414
415 old_notebook->detach_tab(page);
416 add_notebook_page(page, -1);
417 // Remove unnecessary references
418 page.unreference();
419
420 // Set default settings for a new page
421 _notebook.set_tab_reorderable(page);
422 _notebook.set_tab_detachable(page);
423}
424
425void DialogNotebook::select_page(Gtk::Widget& page) {
426 auto pos = _notebook.page_num(page);
427 _notebook.set_current_page(pos);
428}
429
430// ============ Notebook callbacks ==============
431
435void DialogNotebook::close_tab(Gtk::Widget* page) {
436 int page_number = _notebook.get_current_page();
437
438 if (page) {
439 page_number = _notebook.page_num(*page);
440 }
441
442 if (dynamic_cast<DialogBase*>(_notebook.get_nth_page(page_number))) {
443 // is this a dialog in a floating window?
444 if (auto window = dynamic_cast<DialogWindow*>(_container->get_root())) {
445 // store state of floating dialog before it gets deleted
446 DialogManager::singleton().store_state(*window);
447 }
448 }
449
450 // Remove page from notebook
451 _notebook.remove_page(page_number);
452
453 if (_notebook.get_n_pages() == 0) {
454 close_notebook();
455 return;
456 }
457
458 // Update tab labels by comparing the sum of their widths to the allocation
459 on_size_allocate_scroll(get_width());
460}
461
465void DialogNotebook::close_notebook()
466{
467 // Search for DialogMultipaned
468 DialogMultipaned *multipaned = dynamic_cast<DialogMultipaned *>(get_parent());
469 if (multipaned) {
470 multipaned->remove(*this);
471 } else if (get_parent()) {
472 std::cerr << "DialogNotebook::close_notebook: Unexpected parent!" << std::endl;
473 }
474}
475
476void DialogNotebook::move_tab_from(DialogNotebook& source, Gtk::Widget& page, int position) {
477 auto& old_notebook = source._notebook;
478
479 // Keep references until re-attachment
480 page.reference();
481
482 old_notebook.detach_tab(page);
483 add_notebook_page(page, position);
484
485 page.unreference();
486
487 // Set default settings for a new page
488 _notebook.set_tab_reorderable(page);
489 _notebook.set_tab_detachable(page);
490
491 if (old_notebook.get_n_pages() == 0) {
492 source.close_notebook();
493 }
494}
495
496Gtk::Widget* DialogNotebook::get_page(int position) {
497 return _notebook.get_nth_page(position);
498}
499
500Gtk::Notebook* DialogNotebook::get_page_notebook(Gtk::Widget& page) {
501 auto parent = page.get_parent();
502 auto notebook = dynamic_cast<Gtk::Notebook*>(parent);
503 if (!notebook && parent) {
504 // page's parent might be a Stack
505 notebook = dynamic_cast<Gtk::Notebook*>(parent->get_parent());
506 }
507 return notebook;
508}
509
510DialogWindow* DialogNotebook::float_tab(Gtk::Widget& page) {
511 // Move page to notebook in new dialog window (attached to active InkscapeWindow).
512 auto inkscape_window = _container->get_inkscape_window();
513 auto window = new DialogWindow(inkscape_window, &page);
514 window->set_visible(true);
515
516 if (_notebook.get_n_pages() == 0) {
517 close_notebook();
518 return window;
519 }
520
521 // Update tab labels by comparing the sum of their widths to the allocation
522 on_size_allocate_scroll(get_width());
523
524 return window;
525}
526
530DialogWindow* DialogNotebook::pop_tab(Gtk::Widget* page) {
531 // Find page.
532 if (!page) {
533 page = _notebook.get_nth_page(_notebook.get_current_page());
534 }
535
536 if (!page) {
537 std::cerr << "DialogNotebook::pop_tab: page not found!" << std::endl;
538 return nullptr;
539 }
540
541 return float_tab(*page);
542}
543
544void DialogNotebook::dock_current_tab(DialogContainer::DockLocation location) {
545 auto page = _notebook.get_nth_page(_notebook.get_current_page());
546 if (!page) return;
547
548 // we need to get hold of the dialog container in the main window
549 // (this instance may be in a floating dialog window)
550 auto wnd = _container->get_inkscape_window();
551 if (!wnd) return;
552 auto container = wnd->get_desktop()->getContainer();
553 if (!container) return;
554
555 container->dock_dialog(*page, *this, location, nullptr, nullptr);
556}
557
558// ========= Signal handlers - notebook =========
559
563void DialogNotebook::on_page_added(Gtk::Widget *page, int page_num)
564{
565 auto dialog = dynamic_cast<DialogBase*>(page);
566
567 // Does current container/window already have such a dialog?
568 if (dialog && _container->has_dialog_of_type(dialog)) {
569 // We already have a dialog of the same type
570
571 // Highlight first dialog
572 auto other_dialog = _container->get_dialog(dialog->get_type());
573 other_dialog->blink();
574
575 // Remove page from notebook
576 _detaching_duplicate = true; // HACK: prevent removing the initial dialog of the same type
577 _notebook.detach_tab(*page);
578 return;
579 } else if (dialog) {
580 // We don't have a dialog of this type
581
582 // Add to dialog list
583 _container->link_dialog(dialog);
584 } else {
585 // This is not a dialog
586 return;
587 }
588
589 auto tab = _tabs.add_tab(dialog->get_name(), dialog->get_icon(), page_num);
590 _tabs.select_tab(*tab);
591
592 // Update tab labels by comparing the sum of their widths to the allocation
593 on_size_allocate_scroll(get_width());
594}
595
599void DialogNotebook::on_page_removed(Gtk::Widget *page, int page_num)
600{
606 if (_detaching_duplicate) {
607 _detaching_duplicate = false;
608 return;
609 }
610
611 // Remove from dialog list
612 DialogBase *dialog = dynamic_cast<DialogBase *>(page);
613 if (dialog) {
614 _container->unlink_dialog(dialog);
615 }
616
617 _tabs.remove_tab_at(page_num);
618 _tabs.select_tab_at(_notebook.get_current_page());
619}
620
621void DialogNotebook::size_allocate_vfunc(int const width, int const height, int const baseline)
622{
623 Gtk::ScrolledWindow::size_allocate_vfunc(width, height, baseline);
624
625 on_size_allocate_scroll(width);
626}
627
632void DialogNotebook::on_size_allocate_scroll(int const width)
633{
634 // magic number
635 static constexpr int MIN_HEIGHT = 60;
636 // set or unset scrollbars to completely hide a notebook
637 // because we have a "blocking" scroll per tab we need to loop to avoid
638 // other page stop out scroll
639 for_each_page(_notebook, [this](Gtk::Widget &page){
640 if (!provide_scroll(page)) {
641 auto const scrolledwindow = get_scrolledwindow(page);
642 if (scrolledwindow) {
643 double height = scrolledwindow->get_allocation().get_height();
644 if (height > 1) {
645 auto property = scrolledwindow->property_vscrollbar_policy();
646 auto const policy = property.get_value();
647 if (height >= MIN_HEIGHT && policy != Gtk::PolicyType::AUTOMATIC) {
648 property.set_value(Gtk::PolicyType::AUTOMATIC);
649 } else if (height < MIN_HEIGHT && policy != Gtk::PolicyType::EXTERNAL) {
650 property.set_value(Gtk::PolicyType::EXTERNAL);
651 } else {
652 // we don't need to update; break
654 }
655 }
656 }
657 }
659 });
660}
661
662// [[nodiscard]] static int measure_min_width(Gtk::Widget const &widget)
663// {
664// int min_width, ignore;
665// widget.measure(Gtk::Orientation::HORIZONTAL, -1, min_width, ignore, ignore, ignore);
666// return min_width;
667// }
668
669void DialogNotebook::on_page_switch(Gtk::Widget *curr_page, guint page) {
670 _tabs.select_tab_at(page);
671 if (auto dialog = dynamic_cast<DialogBase*>(curr_page)) {
672 dialog->focus_dialog();
673 }
674}
675
676bool DialogNotebook::on_scroll_event(double dx, double dy)
677{
678 if (_notebook.get_n_pages() <= 1) {
679 return false;
680 }
681
682 if (dy < 0) {
683 auto current_page = _notebook.get_current_page();
684 if (current_page > 0) {
685 _notebook.set_current_page(current_page - 1);
686 }
687 } else if (dy > 0) {
688 auto current_page = _notebook.get_current_page();
689 if (current_page < _notebook.get_n_pages() - 1) {
690 _notebook.set_current_page(current_page + 1);
691 }
692 }
693 return true;
694}
695
699void DialogNotebook::change_page(size_t pagenum)
700{
701 _notebook.set_current_page(pagenum);
702}
703
704void DialogNotebook::measure_vfunc(Gtk::Orientation orientation, int for_size, int &min, int &nat, int &min_baseline, int &nat_baseline) const
705{
706 Gtk::ScrolledWindow::measure_vfunc(orientation, for_size, min, nat, min_baseline, nat_baseline);
707 if (orientation == Gtk::Orientation::VERTICAL && _natural_height > 0) {
708 nat = _natural_height;
709 min = std::min(min, _natural_height);
710 }
711}
712
713void DialogNotebook::set_requested_height(int height) {
714 _natural_height = height;
715}
716
717} // namespace Inkscape::UI::Dialog
718
719/*
720 Local Variables:
721 mode:c++
722 c-file-style:"stroustrup"
723 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
724 indent-tabs-mode:nil
725 fill-column:99
726 End:
727*/
728// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
uint64_t page
Definition canvas.cpp:171
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
DialogBase is the base class for the dialog system.
Definition dialog-base.h:42
void blink()
Highlight notebook where dialog already exists.
A widget that manages DialogNotebook's and other widgets inside a horizontal DialogMultipaned contain...
void remove(Gtk::Widget &child)
Removes a widget from DialogMultipaned.
A widget that wraps a Gtk::Notebook with dialogs as pages.
void on_page_added(Gtk::Widget *page, int page_num)
Signal handler to update dialog list when adding a page.
UI::Widget::PopoverMenu _menu_dialogs
std::vector< sigc::scoped_connection > _conn
static std::list< DialogNotebook * > _instances
void on_page_removed(Gtk::Widget *page, int page_num)
Signal handler to update dialog list when removing a page.
void build_docking_menu(UI::Widget::PopoverMenu &menu)
void close_notebook()
Shutdown callback - delete the parent DialogMultipaned before destructing.
UI::Widget::PopoverMenu _menu_tab_ctx
Gtk::Widget * get_page(int position)
void build_dialog_menu(UI::Widget::PopoverMenu &menu)
DialogNotebook(DialogContainer *container)
DialogNotebook constructor.
bool on_scroll_event(double dx, double dy)
void on_page_switch(Gtk::Widget *page, guint page_number)
DialogWindow holds DialogContainer instances for undocked dialogs.
InkscapeWindow * get_inkscape_window()
A replacement for GTK3ʼs Gtk::Menu, as removed in GTK4.
void attach(Gtk::Widget &child, int left_attach, int right_attach, int top_attach, int bottom_attach)
Add child at pos as per Gtk::Menu::attach()
Widget that implements strip of tabs.
Definition tab-strip.h:36
void set_tabs_context_popup(Gtk::Popover *popover)
sigc::signal< void(Gtk::Widget &)> signal_select_tab()
Definition tab-strip.h:72
sigc::signal< void(Gtk::Widget &)> signal_float_tab()
Definition tab-strip.h:76
void set_show_labels(ShowLabels labels)
int get_tab_position(const Gtk::Widget &tab) const
void set_show_close_button(bool show)
void set_new_tab_popup(Gtk::Popover *popover)
void select_tab(const Gtk::Widget &tab)
sigc::signal< void(Gtk::Widget &)> signal_close_tab()
Definition tab-strip.h:74
sigc::signal< void(Gtk::Widget &, int, TabStrip &, int)> signal_move_tab()
Definition tab-strip.h:78
Inkscape::UI::Dialog::DialogContainer * getContainer()
Definition desktop.cpp:335
A base class for all dialogs.
A widget that manages DialogNotebook's and other widgets inside a horizontal DialogMultipaned.
std::span< const DialogData > get_dialog_data_list()
const std::map< std::string, DialogData > & get_dialog_data()
Get the data about all existing dialogs.
Basic dialog info.
char const *const dialog_categories[DialogData::_num_categories]
Definition dialog-data.h:35
A widget with multiple panes.
A wrapper for Gtk::Notebook.
A window for floating docks.
static char const *const parent
Definition dir-util.cpp:70
@ PREFS_NOTEBOOK_LABELS_AUTO
Definition enums.h:149
@ PREFS_NOTEBOOK_LABELS_ACTIVE
Definition enums.h:150
Icon Loader.
SPItem * item
Inkscape - An SVG editor.
Dialog code.
Definition desktop.h:117
static const Glib::Quark dialog_notebook_id("dialog-notebook-id")
DialogNotebook * find_dialog_notebook(Widget::TabStrip *tabs)
Gtk::Widget * find_dialog_page(Widget::TabStrip *tabs, int position)
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
Definition util.cpp:156
Gtk::Widget * for_each_child(Gtk::Widget &widget, Func &&func, bool const plus_self=false, bool const recurse=false, int const level=0)
Call Func with a reference to each child of parent, until it returns _break.
Definition util.h:103
Gtk::Widget * for_each_page(Gtk::Notebook &notebook, Func &&func)
Similar to for_each_child, but only iterates over pages in a notebook.
Definition util.h:152
static cairo_user_data_key_t key
A replacement for GTK3ʼs Gtk::MenuItem, as removed in GTK4.
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
static const Point data[]
int index
SPDesktop * desktop
double height
double width
Glib::RefPtr< Gtk::Builder > builder