gpanthera.cc
C++ source, ASCII text
1
#include "gpanthera.hh"
2
#include <iostream>
3
#include <utility>
4
#include <libintl.h>
5
#include <locale.h>
6
#include <filesystem>
7
#define _(STRING) gettext(STRING)
8
9
namespace gPanthera {
10
std::vector<Gtk::Widget*> collect_children(Gtk::Widget &widget) {
11
std::vector<Gtk::Widget*> children;
12
for(auto *child = widget.get_first_child(); child; child = child->get_next_sibling()) {
13
children.push_back(child);
14
}
15
return children;
16
}
17
18
void init() {
19
// Set up gettext configuration
20
setlocale(LC_ALL, "");
21
22
bindtextdomain("gpanthera", "./locales");
23
textdomain("gpanthera");
24
}
25
26
DockablePane::DockablePane(std::shared_ptr<LayoutManager> layout, Gtk::Widget &child, const Glib::ustring &name, const Glib::ustring &label, Gtk::Image *icon, DockStack *stack, Gtk::Widget *custom_header)
27
: Gtk::Box(Gtk::Orientation::VERTICAL, 0), name(name) {
28
if(icon) {
29
this->icon = icon;
30
}
31
if(stack) {
32
this->stack = stack;
33
}
34
this->layout = std::move(layout);
35
this->label.set_text(label);
36
header = std::make_unique<Gtk::HeaderBar>();
37
header->set_show_title_buttons(false);
38
if(custom_header) {
39
header->set_title_widget(*custom_header);
40
} else {
41
header->set_title_widget(this->label);
42
}
43
auto header_menu_button = Gtk::make_managed<Gtk::MenuButton>();
44
auto header_menu = Gio::Menu::create();
45
header_menu_button->set_direction(Gtk::ArrowType::NONE);
46
header_menu_button->set_menu_model(header_menu);
47
48
auto action_group = Gio::SimpleActionGroup::create();
49
header_menu_button->insert_action_group("win", action_group);
50
51
// Close action
52
auto close_action = Gio::SimpleAction::create("close");
53
close_action->signal_activate().connect([this](const Glib::VariantBase&) {
54
if(this->stack) {
55
this->stack->set_visible_child("empty");
56
}
57
});
58
action_group->add_action(close_action);
59
header_menu->append(_("Close"), "win.close");
60
61
// Pop out action
62
auto pop_out_action = Gio::SimpleAction::create("pop_out");
63
pop_out_action->signal_activate().connect([this](const Glib::VariantBase&) {
64
if(this->stack) {
65
this->pop_out();
66
}
67
});
68
action_group->add_action(pop_out_action);
69
header_menu->append(_("Pop out"), "win.pop_out");
70
71
// Move menu
72
auto move_menu = Gio::Menu::create();
73
for(auto &this_stack : this->layout->stacks) {
74
auto action_name = "move_" + this_stack->name;
75
auto move_action = Gio::SimpleAction::create(action_name);
76
move_action->signal_activate().connect([this, this_stack](const Glib::VariantBase&) {
77
this_stack->add_pane(*this);
78
});
79
action_group->add_action(move_action);
80
move_menu->append(this_stack->name, "win." + action_name);
81
}
82
83
// Add move section
84
auto move_menu_item = Gio::Menu::create();
85
move_menu_item->append_section(_("Move"), move_menu);
86
header_menu->append_section({}, move_menu_item);
87
88
header->pack_end(*header_menu_button);
89
90
this->prepend(*header);
91
this->child = &child;
92
this->append(child);
93
}
94
95
void DockablePane::redock(DockStack *stack) {
96
if(this->window != nullptr) {
97
this->window->hide();
98
this->window->unset_titlebar();
99
this->window->set_decorated(false);
100
this->header->get_style_context()->remove_class("titlebar");
101
this->prepend(*this->header);
102
this->window->unset_child();
103
this->window->close();
104
} else if(this->stack == this->get_parent()) {
105
this->stack->remove(*this);
106
}
107
this->stack = stack;
108
this->stack->add(*this, this->get_identifier());
109
if(this->window != nullptr) {
110
this->window->destroy();
111
delete this->window;
112
this->window = nullptr;
113
}
114
}
115
116
void DockablePane::pop_out() {
117
if(this->stack != nullptr) {
118
this->stack->remove(*this);
119
this->stack = nullptr;
120
}
121
122
if(this->window == nullptr) {
123
this->remove(*this->header);
124
this->window = new DockWindow(this);
125
this->window->set_titlebar(*this->header);
126
this->window->set_child(*this);
127
this->window->set_decorated(true);
128
this->window->present();
129
}
130
}
131
132
Glib::ustring DockablePane::get_identifier() const {
133
return name;
134
}
135
136
Gtk::Image *DockablePane::get_icon() const {
137
return icon;
138
}
139
140
Gtk::Widget *DockablePane::get_child() const {
141
return child;
142
}
143
144
Gtk::Label *DockablePane::get_label() {
145
return &label;
146
}
147
148
LayoutManager::LayoutManager() : Glib::ObjectBase("LayoutManager") {
149
}
150
151
void LayoutManager::add_pane(DockablePane *pane) {
152
panes.push_back(pane);
153
}
154
155
void LayoutManager::add_stack(DockStack *stack) {
156
stacks.push_back(stack);
157
}
158
159
DockStack::DockStack(std::shared_ptr<LayoutManager> layout, const Glib::ustring &name) : Gtk::Stack(), layout(layout), name(name) {
160
auto *empty_child = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
161
add(*empty_child, "empty");
162
// Add the stack to a layout manager
163
this->layout->add_stack(this);
164
}
165
166
DockWindow::DockWindow(DockablePane *pane) {
167
this->pane = pane;
168
this->set_child(*pane);
169
}
170
171
DockStackSwitcher::DockStackSwitcher(DockStack *stack) : Gtk::Box(Gtk::Orientation::HORIZONTAL), stack(stack) {
172
auto update_callback = [this](Gtk::Widget*) {
173
this->update_buttons();
174
};
175
stack->signal_child_added.connect(update_callback);
176
stack->signal_child_removed.connect(update_callback);
177
}
178
179
DockStackSwitcher::~DockStackSwitcher() {
180
add_handler.disconnect();
181
remove_handler.disconnect();
182
}
183
184
DockButton::DockButton(DockablePane *pane) : Gtk::Button(), pane(pane) {
185
if(pane->get_icon()) {
186
auto new_image = Gtk::make_managed<Gtk::Image>(this->pane->get_icon()->get_paintable());
187
this->set_child(*new_image);
188
}
189
this->set_tooltip_text(pane->get_label()->get_text());
190
this->set_halign(Gtk::Align::CENTER);
191
}
192
193
void DockStackSwitcher::update_buttons() {
194
auto old_buttons = collect_children(*this);
195
for(auto *button : old_buttons) {
196
remove(*button);
197
}
198
for(auto *widget = stack->get_first_child(); widget; widget = widget->get_next_sibling()) {
199
if(auto pane = dynamic_cast<DockablePane*>(widget)) {
200
auto *button = Gtk::make_managed<DockButton>(pane);
201
button->signal_clicked().connect([this, pane]() {
202
if(stack->get_visible_child_name() == pane->get_identifier()) {
203
stack->set_visible_child("empty");
204
} else {
205
stack->set_visible_child(pane->get_identifier());
206
}
207
});
208
if(pane->get_identifier() != Glib::ustring("empty")) {
209
append(*button);
210
}
211
}
212
}
213
}
214
215
void DockStack::add_pane(DockablePane &child) {
216
child.redock(this);
217
}
218
219
void DockStack::add(Gtk::Widget &child, const Glib::ustring &name) {
220
Gtk::Stack::add(child, name);
221
signal_child_added.emit(&child);
222
}
223
224
void DockStack::remove(Gtk::Widget &child) {
225
Gtk::Stack::remove(child);
226
signal_child_removed.emit(&child);
227
}
228
} // namespace gPanthera
229