Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
pack.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Daniel Boles <dboles.src+inkscape@gmail.com>
8 *
9 * Copyright (C) 2023 Daniel Boles
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13// The hilarious pack() herein replicates how GTK3ʼs Box can have start or end-
14// packed children, in a way that will be forward-compatible with GTK4, wherein
15// Box is far simpler & just prepends/appends to a single group of children. We
16// cannot replace pack_start|end() with prepend|append(), since not only do they
17// lose the expand/fill args, but also the 2 sets of methods order children in
18// reverse order to each other, & GTK4 does not separate the 2 sets of children.
19// Here, I fix this by retaining an unordered_map from known Boxes to start-side
20// children, adding/removing in same when any start-side child is added/removed…
21// then when asked to pack a child at either side, using the count of start-side
22// chldren to determine the appropriate position at which to add() that child.
23// GTK3 child properties are emulated by normal properties on the child widget.
24
25#include "ui/pack.h"
26
27#include <algorithm>
28#include <cstdlib>
29#include <unordered_map>
30#include <gtkmm/box.h>
31
32#include <sigc++/scoped_connection.h>
33#include "ui/util.h"
34
35namespace Inkscape::UI {
36
37enum class PackType {start, end};
38
39using BoxChildren = std::unordered_map<Gtk::Widget *, sigc::scoped_connection>;
40static auto s_box_children = std::unordered_map<Gtk::Box *, BoxChildren>{};
41
42static void set_expand(Gtk::Widget &widget, Gtk::Orientation const orientation,
43 bool const expand)
44{
45 switch (orientation) {
46 case Gtk::Orientation::HORIZONTAL: widget.set_hexpand(expand); break;
47 case Gtk::Orientation::VERTICAL : widget.set_vexpand(expand); break;
48 default: std::abort();
49 }
50}
51
52static void set_align(Gtk::Widget &widget, Gtk::Orientation const orientation,
53 Gtk::Align const align)
54{
55 switch (orientation) {
56 case Gtk::Orientation::HORIZONTAL: widget.set_halign(align); break;
57 case Gtk::Orientation::VERTICAL : widget.set_valign(align); break;
58 default: std::abort();
59 }
60}
61
62[[nodiscard]] static auto to_align(PackType const pack_type)
63{
64 switch (pack_type) {
65 case PackType::start: return Gtk::Align::START;
66 case PackType::end : return Gtk::Align::END ;
67 default: std::abort();
68 }
69}
70
71static void set_fill(Gtk::Widget &widget, Gtk::Orientation const orientation,
72 bool const fill, PackType const pack_type)
73{
74 auto const align = fill ? Gtk::Align::FILL : to_align(pack_type);
75 set_align(widget, orientation, align);
76}
77
78static void set_padding(Gtk::Widget &widget, Gtk::Orientation const orientation,
79 int const margin_start, int const margin_end)
80{
81 switch (orientation) {
82 case Gtk::Orientation::HORIZONTAL:
83 widget.set_margin_start(widget.get_margin_start() + margin_start);
84 widget.set_margin_end (widget.get_margin_end () + margin_end );
85 break;
86 case Gtk::Orientation::VERTICAL:
87 widget.set_margin_top (widget.get_margin_top () + margin_start);
88 widget.set_margin_bottom(widget.get_margin_bottom() + margin_end );
89 break;
90 default: std::abort();
91 }
92}
93
94static void add(Gtk::Box &box, PackType const pack_type, Gtk::Widget &child)
95{
96 auto const [it, inserted] = s_box_children.emplace(&box, BoxChildren{});
97 // macOS runner errors if lambda captures structured binding. C++ Defect Report says this is OK
98 auto &starts = it->second;
99
100 if (inserted) {
101 box.signal_destroy().connect([&]{ s_box_children.erase(&box); });
102 }
103
104 if (starts.empty()) {
105 box.prepend(child); // Prepend so PackType::end arranges children from end-to-start as GTK3
106 } else {
107 auto const position = starts.size();
108 auto &previous = get_nth_child(box, position - 1);
109 box.append(child);
110 box.reorder_child_after(child, previous);
111 }
112
113 if (pack_type != PackType::start) return;
114
115 // Add the child to our list of start ones, and! connect ::parent changed to remove that later.
116 auto const erase_child = [&]{ starts.erase(&child); };
117 auto connection = child.property_parent().signal_changed().connect(erase_child);
118 starts.emplace(&child, std::move(connection));
119}
120
121static void pack(PackType const pack_type,
122 Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill,
123 unsigned const padding)
124{
125 auto const orientation = box.get_orientation();
126 set_expand (child, orientation, expand );
127 set_fill (child, orientation, fill , pack_type);
128 set_padding(child, orientation, padding, padding );
129 add(box, pack_type, child);
130}
131
132static void pack(PackType const pack_type,
133 Gtk::Box &box, Gtk::Widget &child, PackOptions const options,
134 unsigned const padding)
135{
136 auto const expand = options != PackOptions::shrink ;
137 auto const fill = options == PackOptions::expand_widget;
138 pack(pack_type, box, child, expand, fill, padding);
139}
140
141void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill,
142 unsigned const padding)
143{
144 pack(PackType::start, box, child, expand, fill, padding);
145}
146
147void pack_start(Gtk::Box &box, Gtk::Widget &child, PackOptions const options,
148 unsigned const padding)
149{
150 pack(PackType::start, box, child, options, padding);
151}
152
153void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill,
154 unsigned const padding)
155{
156 pack(PackType::end, box, child, expand, fill, padding);
157}
158
159void pack_end(Gtk::Box &box, Gtk::Widget &child, PackOptions const options,
160 unsigned const padding)
161{
162 pack(PackType::end, box, child, options, padding);
163}
164
165} // namespace Inkscape::UI
166
167/*
168 Local Variables:
169 mode:c++
170 c-file-style:"stroustrup"
171 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
172 indent-tabs-mode:nil
173 fill-column:99
174 End:
175*/
176// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
User interface code.
Definition desktop.h:113
static void set_fill(Gtk::Widget &widget, Gtk::Orientation const orientation, bool const fill, PackType const pack_type)
Definition pack.cpp:71
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
Definition pack.cpp:153
static auto to_align(PackType const pack_type)
Definition pack.cpp:62
Gtk::Widget & get_nth_child(Gtk::Widget &widget, std::size_t const index)
Get the widgetʼs child at the given position. Throws std::out_of_range if the index is invalid.
Definition util.cpp:165
static void set_expand(Gtk::Widget &widget, Gtk::Orientation const orientation, bool const expand)
Definition pack.cpp:42
PackOptions
Equivalent to GTK3ʼs Gtk::PackOptions.
Definition pack.h:24
static void set_align(Gtk::Widget &widget, Gtk::Orientation const orientation, Gtk::Align const align)
Definition pack.cpp:52
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
static void add(Gtk::Box &box, PackType const pack_type, Gtk::Widget &child)
Definition pack.cpp:94
static auto s_box_children
Definition pack.cpp:40
static void pack(PackType const pack_type, Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Definition pack.cpp:121
std::unordered_map< Gtk::Widget *, sigc::scoped_connection > BoxChildren
Definition pack.cpp:39
static void set_padding(Gtk::Widget &widget, Gtk::Orientation const orientation, int const margin_start, int const margin_end)
Definition pack.cpp:78
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Ocnode * child[8]
Definition quantize.cpp:33