basalt-gtk 0.0.0-rc5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/cli/runLinux.js +29 -0
- package/cli/shared.js +75 -0
- package/native/CMakeLists.txt +362 -0
- package/native/gtk/ColorSchemeGtk.cpp +58 -0
- package/native/gtk/ComponentRegistryGtk.cpp +101 -0
- package/native/gtk/FontRegistryFontconfig.cpp +127 -0
- package/native/gtk/GtkAnimationChoreographer.cpp +73 -0
- package/native/gtk/GtkAnimationChoreographer.h +49 -0
- package/native/gtk/GtkDropTarget.cpp +269 -0
- package/native/gtk/GtkDropTarget.h +40 -0
- package/native/gtk/GtkFocus.cpp +296 -0
- package/native/gtk/GtkFocus.h +113 -0
- package/native/gtk/GtkImageLoader.cpp +128 -0
- package/native/gtk/GtkImageLoader.h +87 -0
- package/native/gtk/GtkKeyEvents.cpp +91 -0
- package/native/gtk/GtkKeyEvents.h +21 -0
- package/native/gtk/GtkMenuBar.cpp +45 -0
- package/native/gtk/GtkMountingManager.cpp +1027 -0
- package/native/gtk/GtkMountingManager.h +146 -0
- package/native/gtk/GtkRunLoopObserver.cpp +77 -0
- package/native/gtk/GtkRunLoopObserver.h +41 -0
- package/native/gtk/GtkScrollView.cpp +626 -0
- package/native/gtk/GtkScrollView.h +209 -0
- package/native/gtk/GtkSkiaContext.cpp +248 -0
- package/native/gtk/GtkSkiaContext.h +62 -0
- package/native/gtk/GtkSkiaModule.cpp +80 -0
- package/native/gtk/GtkSkiaModule.h +51 -0
- package/native/gtk/GtkSkiaPeer.cpp +285 -0
- package/native/gtk/GtkSkiaPeer.h +63 -0
- package/native/gtk/GtkTextInput.cpp +428 -0
- package/native/gtk/GtkTextInput.h +123 -0
- package/native/gtk/GtkTextPeer.cpp +425 -0
- package/native/gtk/GtkTextPeer.h +87 -0
- package/native/gtk/GtkTitleBar.cpp +273 -0
- package/native/gtk/GtkTitleBar.h +96 -0
- package/native/gtk/GtkTitleBarLayout.cpp +45 -0
- package/native/gtk/GtkTitleBarLayout.h +36 -0
- package/native/gtk/GtkTouchDispatcher.cpp +671 -0
- package/native/gtk/GtkTouchDispatcher.h +139 -0
- package/native/gtk/GtkWindowControl.cpp +223 -0
- package/native/gtk/GtkWindowModule.cpp +194 -0
- package/native/gtk/GtkWindowModule.h +62 -0
- package/native/gtk/PangoTextLayout.cpp +295 -0
- package/native/gtk/PangoTextLayout.h +54 -0
- package/native/gtk/PangoTextLayoutManager.cpp +140 -0
- package/native/gtk/PlatformServicesGtk.cpp +583 -0
- package/native/gtk/RnView.cpp +1407 -0
- package/native/gtk/RnView.h +339 -0
- package/native/gtk/demo_layout_gtk.cpp +57 -0
- package/native/gtk/main_gtk.cpp +1981 -0
- package/native/gtk/mount_harness_gtk.cpp +143 -0
- package/native/tests/TestMainGtk.cpp +25 -0
- package/native/tests/notification_stub.cpp +103 -0
- package/native/tests/test_accessibility.cpp +251 -0
- package/native/tests/test_focus.cpp +194 -0
- package/native/tests/test_hittest.cpp +242 -0
- package/native/tests/test_image.cpp +135 -0
- package/native/tests/test_key_events_gtk.cpp +112 -0
- package/native/tests/test_mounting.cpp +415 -0
- package/native/tests/test_scrollview.cpp +258 -0
- package/native/tests/test_skia_texture.cpp +120 -0
- package/native/tests/test_text.cpp +147 -0
- package/native/tests/test_textinput.cpp +598 -0
- package/native/tests/test_titlebar.cpp +137 -0
- package/native/tests/test_titlebar_drag.cpp +138 -0
- package/native/tests/test_version.cpp +49 -0
- package/native/tests/test_view.cpp +162 -0
- package/native/tests/test_viewprops.cpp +254 -0
- package/package.json +26 -0
- package/react-native.config.js +21 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#include "GtkTitleBar.h"
|
|
2
|
+
|
|
3
|
+
#include <cstdio>
|
|
4
|
+
|
|
5
|
+
namespace basalt {
|
|
6
|
+
|
|
7
|
+
namespace {
|
|
8
|
+
|
|
9
|
+
// processColor's 0xAARRGGBB as a CSS colour. Alpha included: a header bar can
|
|
10
|
+
// be translucent where a Windows caption cannot.
|
|
11
|
+
std::string cssColor(uint32_t argb) {
|
|
12
|
+
char buffer[64];
|
|
13
|
+
std::snprintf(buffer,
|
|
14
|
+
sizeof(buffer),
|
|
15
|
+
"rgba(%u,%u,%u,%.3f)",
|
|
16
|
+
(argb >> 16) & 0xFFu,
|
|
17
|
+
(argb >> 8) & 0xFFu,
|
|
18
|
+
argb & 0xFFu,
|
|
19
|
+
static_cast<double>((argb >> 24) & 0xFFu) / 255.0);
|
|
20
|
+
return buffer;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
void GtkTitleBar::attach(GtkWindow *window, GtkWidget *controls) {
|
|
26
|
+
// Weak, so these become NULL when the widgets go rather than dangling.
|
|
27
|
+
//
|
|
28
|
+
// This outlives the window: it is a process-wide singleton and the window is
|
|
29
|
+
// destroyed at quit, or by close() below, or by the user. Every method here
|
|
30
|
+
// is reachable from JavaScript, which is still running at that point -- so a
|
|
31
|
+
// raw pointer is a use-after-free waiting for a late setTitle. AppKit's half
|
|
32
|
+
// gets this for free from __weak; GTK has to ask.
|
|
33
|
+
if (window_ != nullptr) {
|
|
34
|
+
g_object_remove_weak_pointer(G_OBJECT(window_), reinterpret_cast<gpointer *>(&window_));
|
|
35
|
+
}
|
|
36
|
+
if (controls_ != nullptr) {
|
|
37
|
+
g_object_remove_weak_pointer(G_OBJECT(controls_), reinterpret_cast<gpointer *>(&controls_));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
window_ = window;
|
|
41
|
+
controls_ = controls;
|
|
42
|
+
header_ = nullptr;
|
|
43
|
+
|
|
44
|
+
if (window_ != nullptr) {
|
|
45
|
+
g_object_add_weak_pointer(G_OBJECT(window_), reinterpret_cast<gpointer *>(&window_));
|
|
46
|
+
}
|
|
47
|
+
if (controls_ != nullptr) {
|
|
48
|
+
g_object_add_weak_pointer(G_OBJECT(controls_), reinterpret_cast<gpointer *>(&controls_));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
apply();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void GtkTitleBar::setTitle(std::optional<std::string> title) {
|
|
55
|
+
title_ = std::move(title);
|
|
56
|
+
apply();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void GtkTitleBar::setColors(std::optional<uint32_t> background,
|
|
60
|
+
std::optional<uint32_t> text,
|
|
61
|
+
std::optional<uint32_t> border) {
|
|
62
|
+
background_ = background;
|
|
63
|
+
text_ = text;
|
|
64
|
+
border_ = border;
|
|
65
|
+
apply();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void GtkTitleBar::setStyle(TitleBarStyle style) {
|
|
69
|
+
if (style_ == style) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
style_ = style;
|
|
73
|
+
apply();
|
|
74
|
+
notify();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
void GtkTitleBar::minimize() {
|
|
78
|
+
if (window_ != nullptr) {
|
|
79
|
+
gtk_window_minimize(window_);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void GtkTitleBar::toggleMaximize() {
|
|
84
|
+
if (window_ == nullptr) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// GTK has a maximised state to read, unlike AppKit's zoom, so this really is
|
|
88
|
+
// a toggle rather than a request to swap frames.
|
|
89
|
+
if (gtk_window_is_maximized(window_)) {
|
|
90
|
+
gtk_window_unmaximize(window_);
|
|
91
|
+
} else {
|
|
92
|
+
gtk_window_maximize(window_);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void GtkTitleBar::close() {
|
|
97
|
+
if (window_ != nullptr) {
|
|
98
|
+
// close() rather than destroy(): it asks, so a close-request handler still
|
|
99
|
+
// gets its say, which is how the real button behaves.
|
|
100
|
+
gtk_window_close(window_);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
void GtkTitleBar::startDrag() {
|
|
105
|
+
// Still nothing to do here, but not for the reason this used to give.
|
|
106
|
+
//
|
|
107
|
+
// It said there is no "begin a drag now" call. There is:
|
|
108
|
+
// gdk_toplevel_begin_move, which beginMoveDrag below uses. What there is no
|
|
109
|
+
// way to supply is its arguments -- the device, the button and the
|
|
110
|
+
// timestamp of the press GDK is being asked to take over. By the time
|
|
111
|
+
// JavaScript can call this, the press has been delivered, handled and
|
|
112
|
+
// forgotten, and a move begun without it is refused by the compositor or
|
|
113
|
+
// starts a drag the pointer is not in.
|
|
114
|
+
//
|
|
115
|
+
// So the conclusion the old comment reached was right even though its
|
|
116
|
+
// premise was wrong: this belongs in the host, at the press. That is where
|
|
117
|
+
// it now is -- main_gtk.cpp hit-tests the drag regions on a click gesture
|
|
118
|
+
// and calls beginMoveDrag. An app marking <TitleBar.DragRegion> needs
|
|
119
|
+
// nothing from this method.
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void GtkTitleBar::beginMoveDrag(GdkDevice *device,
|
|
123
|
+
int button,
|
|
124
|
+
double x,
|
|
125
|
+
double y,
|
|
126
|
+
guint32 timestamp) {
|
|
127
|
+
// window_ is a weak pointer and the device comes from an event that has
|
|
128
|
+
// already been dispatched, so both can be gone by the time this runs.
|
|
129
|
+
if (window_ == nullptr || device == nullptr) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
GdkSurface *const surface = gtk_native_get_surface(GTK_NATIVE(window_));
|
|
134
|
+
// Not every GdkSurface is a toplevel -- a popup is not -- and only a
|
|
135
|
+
// toplevel can be moved. A window that has not been mapped yet has no
|
|
136
|
+
// surface at all, which is what an app calling this during its first render
|
|
137
|
+
// would reach.
|
|
138
|
+
if (surface == nullptr || !GDK_IS_TOPLEVEL(surface)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
gdk_toplevel_begin_move(GDK_TOPLEVEL(surface), device, button, x, y, timestamp);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
TitleBarMetrics GtkTitleBar::metrics() const {
|
|
146
|
+
TitleBarMetrics result;
|
|
147
|
+
result.style = style_;
|
|
148
|
+
if (style_ == TitleBarStyle::Native || controls_ == nullptr) {
|
|
149
|
+
// GTK's own decorations sit above the child rather than over it, so they
|
|
150
|
+
// take nothing from the app and there is nothing to report.
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Measured rather than assumed: how tall the buttons are and how much room
|
|
155
|
+
// they need is the theme's business, and a hard-coded 32 would be wrong on
|
|
156
|
+
// every desktop that disagrees.
|
|
157
|
+
int minimum = 0;
|
|
158
|
+
int natural = 0;
|
|
159
|
+
gtk_widget_measure(controls_, GTK_ORIENTATION_HORIZONTAL, -1, &minimum, &natural, nullptr, nullptr);
|
|
160
|
+
result.buttonsWidth = natural;
|
|
161
|
+
gtk_widget_measure(controls_, GTK_ORIENTATION_VERTICAL, -1, &minimum, &natural, nullptr, nullptr);
|
|
162
|
+
result.height = natural;
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void GtkTitleBar::setMetricsListener(std::function<void(const TitleBarMetrics &)> listener) {
|
|
167
|
+
listener_ = std::move(listener);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void GtkTitleBar::apply() {
|
|
171
|
+
if (window_ == nullptr) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (title_.has_value()) {
|
|
176
|
+
gtk_window_set_title(window_, title_->c_str());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const bool hidden = style_ == TitleBarStyle::Hidden;
|
|
180
|
+
// The header bar goes away with the decorations rather than being left
|
|
181
|
+
// behind empty, which is what `set_decorated(FALSE)` alone would do on a
|
|
182
|
+
// window that has one.
|
|
183
|
+
if (header_ != nullptr) {
|
|
184
|
+
gtk_widget_set_visible(header_, hidden ? FALSE : TRUE);
|
|
185
|
+
}
|
|
186
|
+
gtk_window_set_decorated(window_, hidden ? FALSE : TRUE);
|
|
187
|
+
if (controls_ != nullptr) {
|
|
188
|
+
// The host's stand-in for the caption buttons the other two systems keep
|
|
189
|
+
// drawing. Only when there are no real ones.
|
|
190
|
+
gtk_widget_set_visible(controls_, hidden ? TRUE : FALSE);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// A header bar of our own, and only once an app has asked for a colour.
|
|
194
|
+
//
|
|
195
|
+
// Without one there is nothing to style: GTK draws a `headerbar` node only
|
|
196
|
+
// when it is doing client-side decorations, and where it is not -- an X11
|
|
197
|
+
// session with server-side decorations, or the quartz backend, where macOS
|
|
198
|
+
// draws the frame -- the caption belongs to something CSS cannot reach.
|
|
199
|
+
//
|
|
200
|
+
// Installed lazily rather than at startup because `set_titlebar` takes
|
|
201
|
+
// vertical space from the child. A window that was never asked about its
|
|
202
|
+
// title bar must be exactly the window it was before this class existed --
|
|
203
|
+
// the first version installed one unconditionally and moved every app's
|
|
204
|
+
// layout down by the height of a header, which two end-to-end scenarios
|
|
205
|
+
// noticed by tapping where a button no longer was.
|
|
206
|
+
// Worth knowing what this costs: a GtkHeaderBar draws GTK's own window
|
|
207
|
+
// controls, so from here the buttons are Adwaita's rather than whatever the
|
|
208
|
+
// window frame had. On the target desktop that is the same thing -- GNOME
|
|
209
|
+
// does client-side decorations, and these *are* the system's buttons. On a
|
|
210
|
+
// backend where something else draws the frame, the quartz one used for
|
|
211
|
+
// development among them, it is visibly GTK's buttons on someone else's
|
|
212
|
+
// window. Unavoidable: CSS is the only handle GTK offers for a caption
|
|
213
|
+
// colour, and it reaches nothing the system drew.
|
|
214
|
+
const bool wantsColour = background_.has_value() || text_.has_value() || border_.has_value();
|
|
215
|
+
if (wantsColour && header_ == nullptr) {
|
|
216
|
+
header_ = gtk_header_bar_new();
|
|
217
|
+
gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(header_), TRUE);
|
|
218
|
+
gtk_window_set_titlebar(window_, header_);
|
|
219
|
+
g_object_add_weak_pointer(G_OBJECT(header_), reinterpret_cast<gpointer *>(&header_));
|
|
220
|
+
gtk_widget_set_visible(header_, hidden ? FALSE : TRUE);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Colours, as CSS on the header bar. There is no property for any of this:
|
|
224
|
+
// a GtkWindow's decorations are styled or they are the theme's.
|
|
225
|
+
if (provider_ == nullptr) {
|
|
226
|
+
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window_));
|
|
227
|
+
if (display == nullptr) {
|
|
228
|
+
// No display yet, so nothing to style and nothing drawn. The next call
|
|
229
|
+
// installs it.
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
provider_ = gtk_css_provider_new();
|
|
233
|
+
gtk_style_context_add_provider_for_display(
|
|
234
|
+
display, GTK_STYLE_PROVIDER(provider_), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
std::string css;
|
|
238
|
+
if (background_.has_value() || text_.has_value() || border_.has_value()) {
|
|
239
|
+
css += "headerbar, headerbar > windowhandle {";
|
|
240
|
+
if (background_.has_value()) {
|
|
241
|
+
// `background-image: none` as well: the theme's header bar is usually a
|
|
242
|
+
// gradient, and a background-color alone leaves it on top.
|
|
243
|
+
css += "background-image: none; background-color: " + cssColor(*background_) + ";";
|
|
244
|
+
}
|
|
245
|
+
if (text_.has_value()) {
|
|
246
|
+
css += "color: " + cssColor(*text_) + ";";
|
|
247
|
+
}
|
|
248
|
+
if (border_.has_value()) {
|
|
249
|
+
// Unlike AppKit, GTK does have somewhere to put this.
|
|
250
|
+
css += "border-bottom: 1px solid " + cssColor(*border_) + ";";
|
|
251
|
+
}
|
|
252
|
+
css += "}";
|
|
253
|
+
// The title label inherits colour from the header bar on most themes and
|
|
254
|
+
// not on all, so it is named too.
|
|
255
|
+
if (text_.has_value()) {
|
|
256
|
+
css += "headerbar label { color: " + cssColor(*text_) + "; }";
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
gtk_css_provider_load_from_string(provider_, css.c_str());
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
void GtkTitleBar::notify() {
|
|
263
|
+
if (listener_) {
|
|
264
|
+
listener_(metrics());
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
GtkTitleBar &titleBar() {
|
|
269
|
+
static GtkTitleBar instance;
|
|
270
|
+
return instance;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
} // namespace basalt
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// The window's title bar on Linux: its title, its colours, and whether GTK
|
|
2
|
+
// draws it or the app does.
|
|
3
|
+
//
|
|
4
|
+
// The same two styles Win32TitleBar.h and AppKitTitleBar.h describe, but GTK's
|
|
5
|
+
// window model is neither of theirs and the hidden style had to be built
|
|
6
|
+
// rather than switched on.
|
|
7
|
+
//
|
|
8
|
+
// - Native style. The window keeps its decorations. GTK draws them as part of
|
|
9
|
+
// the window (client-side), but always *above* the child rather than over
|
|
10
|
+
// it, so they cost the app nothing and every metric is zero -- the same
|
|
11
|
+
// zeroes the other two report, arrived at differently. Colours are CSS on
|
|
12
|
+
// the `headerbar` node, which is the only handle GTK offers.
|
|
13
|
+
//
|
|
14
|
+
// - Hidden style. `gtk_window_set_decorated(FALSE)` takes the whole titlebar
|
|
15
|
+
// away, buttons included -- which is where GTK parts company with the other
|
|
16
|
+
// two, whose systems keep drawing the caption buttons over the app's
|
|
17
|
+
// content. So the host draws them: a GtkWindowControls in a GtkOverlay above
|
|
18
|
+
// the surface root, which is the same picture by different means, and the
|
|
19
|
+
// metrics describe it.
|
|
20
|
+
//
|
|
21
|
+
// GTK main thread only, like the window. The BasaltWindow module posts here.
|
|
22
|
+
|
|
23
|
+
#pragma once
|
|
24
|
+
|
|
25
|
+
#include <gtk/gtk.h>
|
|
26
|
+
|
|
27
|
+
#include <cstdint>
|
|
28
|
+
#include <functional>
|
|
29
|
+
#include <optional>
|
|
30
|
+
#include <string>
|
|
31
|
+
|
|
32
|
+
namespace basalt {
|
|
33
|
+
|
|
34
|
+
enum class TitleBarStyle { Native, Hidden };
|
|
35
|
+
|
|
36
|
+
// What JavaScript is told, in logical pixels -- the surface root's units.
|
|
37
|
+
struct TitleBarMetrics {
|
|
38
|
+
double height{0};
|
|
39
|
+
double buttonsWidth{0};
|
|
40
|
+
TitleBarStyle style{TitleBarStyle::Native};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
class GtkTitleBar {
|
|
44
|
+
public:
|
|
45
|
+
// The window, and the controls the host overlays when the style is hidden.
|
|
46
|
+
// Both borrowed; the host owns them for the life of the process.
|
|
47
|
+
void attach(GtkWindow *window, GtkWidget *controls);
|
|
48
|
+
|
|
49
|
+
void setTitle(std::optional<std::string> title);
|
|
50
|
+
// 0xAARRGGBB as processColor produces, or nullopt for GTK's own.
|
|
51
|
+
void setColors(std::optional<uint32_t> background,
|
|
52
|
+
std::optional<uint32_t> text,
|
|
53
|
+
std::optional<uint32_t> border);
|
|
54
|
+
void setStyle(TitleBarStyle style);
|
|
55
|
+
|
|
56
|
+
// What the caption buttons do, for an app that draws its own. The system's
|
|
57
|
+
// buttons work without any of this; a header the app drew does not.
|
|
58
|
+
void minimize();
|
|
59
|
+
void toggleMaximize();
|
|
60
|
+
void close();
|
|
61
|
+
void startDrag();
|
|
62
|
+
|
|
63
|
+
// Hands the window to the window manager's own move loop, from the press
|
|
64
|
+
// that started it.
|
|
65
|
+
//
|
|
66
|
+
// `x` and `y` are in surface coordinates, and the device, button and
|
|
67
|
+
// timestamp are the press's own: GDK will not begin a move without them,
|
|
68
|
+
// which is the whole reason this takes arguments where startDrag() above
|
|
69
|
+
// cannot. The host calls it from the gesture; see main_gtk.cpp.
|
|
70
|
+
void beginMoveDrag(GdkDevice *device, int button, double x, double y, guint32 timestamp);
|
|
71
|
+
|
|
72
|
+
TitleBarMetrics metrics() const;
|
|
73
|
+
|
|
74
|
+
void setMetricsListener(std::function<void(const TitleBarMetrics &)> listener);
|
|
75
|
+
|
|
76
|
+
private:
|
|
77
|
+
void apply();
|
|
78
|
+
void notify();
|
|
79
|
+
|
|
80
|
+
GtkWindow *window_{nullptr};
|
|
81
|
+
GtkWidget *controls_{nullptr};
|
|
82
|
+
// The header bar this class installs, so there is something to style. See
|
|
83
|
+
// attach().
|
|
84
|
+
GtkWidget *header_{nullptr};
|
|
85
|
+
GtkCssProvider *provider_{nullptr};
|
|
86
|
+
std::optional<std::string> title_;
|
|
87
|
+
std::optional<uint32_t> background_;
|
|
88
|
+
std::optional<uint32_t> text_;
|
|
89
|
+
std::optional<uint32_t> border_;
|
|
90
|
+
TitleBarStyle style_{TitleBarStyle::Native};
|
|
91
|
+
std::function<void(const TitleBarMetrics &)> listener_;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
GtkTitleBar &titleBar();
|
|
95
|
+
|
|
96
|
+
} // namespace basalt
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#include "GtkTitleBarLayout.h"
|
|
2
|
+
|
|
3
|
+
#include "TitleBarRegions.h"
|
|
4
|
+
|
|
5
|
+
namespace basalt {
|
|
6
|
+
|
|
7
|
+
bool isTitleBarDragRegionAt(RnView *root, double x, double y) {
|
|
8
|
+
if (root == nullptr || !RN_IS_VIEW(root)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
GtkWidget *const rootWidget = GTK_WIDGET(root);
|
|
13
|
+
// The toolkit's own hit test, in the root's coordinates. NULL when the point
|
|
14
|
+
// is outside the root or lands on nothing targetable, and the loop below
|
|
15
|
+
// then simply does not run -- which is the same answer Windows gives for a
|
|
16
|
+
// miss: not a drag.
|
|
17
|
+
GtkWidget *widget = gtk_widget_pick(rootWidget, x, y, GTK_PICK_DEFAULT);
|
|
18
|
+
|
|
19
|
+
for (; widget != nullptr; widget = gtk_widget_get_parent(widget)) {
|
|
20
|
+
// The chain from a picked widget is not all RnViews. A <TextInput>'s peer
|
|
21
|
+
// is a GtkText, a <Switch> is a GtkSwitch, and neither carries a nativeID
|
|
22
|
+
// -- so they are stepped over rather than stopping the walk, and the
|
|
23
|
+
// marked ancestor above them still decides.
|
|
24
|
+
if (RN_IS_VIEW(widget)) {
|
|
25
|
+
const char *id = rn_view_get_native_id(RN_VIEW(widget));
|
|
26
|
+
if (g_strcmp0(id, kTitleBarDragRegionId) == 0) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (g_strcmp0(id, kTitleBarNoDragRegionId) == 0) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// The root is the last thing asked, not the first thing skipped: an app
|
|
35
|
+
// that marks its whole surface as a drag region is answered here. Above it
|
|
36
|
+
// lies the host's own overlay, which is not the app's to mark.
|
|
37
|
+
if (widget == rootWidget) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
} // namespace basalt
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Whether a point in an app-drawn header drags the window.
|
|
2
|
+
//
|
|
3
|
+
// The geometry half of GtkTitleBar, kept apart from it for the same reason
|
|
4
|
+
// Win32TitleBarLayout is kept apart from Win32TitleBar: it needs a view tree
|
|
5
|
+
// and nothing else -- no window, no toplevel, no React Native -- so it can be
|
|
6
|
+
// tested with none of them. What a custom caption gets wrong is exactly this
|
|
7
|
+
// -- a label that will not drag, a button that drags instead of pressing --
|
|
8
|
+
// and it is invisible to every other test.
|
|
9
|
+
//
|
|
10
|
+
// There is no GTK equivalent of the caption-button arithmetic that fills out
|
|
11
|
+
// the Windows file. GTK takes the whole titlebar away with the decorations and
|
|
12
|
+
// the host puts a real GtkWindowControls back, so the buttons are widgets that
|
|
13
|
+
// place and hit-test themselves. Only the drag question is left.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
|
|
17
|
+
#include "RnView.h"
|
|
18
|
+
|
|
19
|
+
namespace basalt {
|
|
20
|
+
|
|
21
|
+
// Whether a point, in the root's coordinates, drags the window.
|
|
22
|
+
//
|
|
23
|
+
// The rule is core/TitleBarRegions.h's, and it is the one Win32 applies: the
|
|
24
|
+
// deepest view under the point and then each ancestor in turn, with the
|
|
25
|
+
// nearest one marked either way deciding. A label inside a drag region drags
|
|
26
|
+
// without being marked, a button inside one stays pressable once it is marked
|
|
27
|
+
// no-drag, and a point under nothing marked is ordinary client area.
|
|
28
|
+
//
|
|
29
|
+
// Unlike the Windows host this does not walk the tree itself -- gtk_widget_pick
|
|
30
|
+
// is the toolkit's own answer to "what is under this point", and it already
|
|
31
|
+
// honours can-target, so a view with pointerEvents="none" is skipped here for
|
|
32
|
+
// the same reason a click would miss it. Writing a second walk would be a
|
|
33
|
+
// second chance to disagree with the toolkit about what was clicked.
|
|
34
|
+
bool isTitleBarDragRegionAt(RnView *root, double x, double y);
|
|
35
|
+
|
|
36
|
+
} // namespace basalt
|