feat(gtk4): GtkGLArea-based plot area and demo
This commit is contained in:
1 parent
3c0e7763ab
commit
36cd3421f3
10 files changed
+1748
-2
No files matched your search
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <plot/core/types.hpp>
|
||||
#include <plot/input/events.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace plot::gtk4 {
|
||||
|
||||
/// GDK's scroll unit (`GdkScrollUnit`), mirrored here so this header -- and a
|
||||
/// test that drives it -- needs no GTK include directory. `gtk_plot_area.cpp`
|
||||
/// static-asserts that the two agree with GDK's own enumerators, so a GDK that
|
||||
/// renumbered them would fail to compile rather than silently mis-scale a zoom.
|
||||
enum class ScrollUnit {
|
||||
Wheel = 0, ///< the delta is already in wheel detents, one per notch
|
||||
Surface = 1, ///< the delta is in surface logical pixels
|
||||
};
|
||||
|
||||
/// Surface logical pixels per wheel notch, for a `ScrollUnit::Surface` delta.
|
||||
///
|
||||
/// GDK reports a smooth device's scroll in surface pixels, the controller takes
|
||||
/// notches, and passing a pixel delta through would make a touchpad's zoom
|
||||
/// unusably sensitive -- a flick is hundreds of pixels, and the controller's one
|
||||
/// notch is ~14% of the span. 120 pixels per notch is the model Wayland's
|
||||
/// `wl_pointer.axis_value120` introduced ("the value 120 corresponds to one
|
||||
/// physical scroll click/detent"), and it is the same 120-per-notch scale a Qt
|
||||
/// `QWheelEvent::angleDelta()` uses (eighths of a degree, 120 of them to a
|
||||
/// notch), so a smooth device zooms at the detent rate its discrete siblings do
|
||||
/// rather than an order of magnitude faster. A delta already reported in wheel
|
||||
/// units is never divided by this.
|
||||
inline constexpr f64 kSurfacePixelsPerNotch = 120.0;
|
||||
|
||||
/// GTK's `GdkModifierType` bits as the controller's `input::Mod` mask.
|
||||
///
|
||||
/// A mask, not a reinterpretation: GDK's state carries the *mouse button* bits
|
||||
/// as well (`GDK_BUTTON1_MASK` is `1 << 8`, and a release reports the button that
|
||||
/// produced it), so the four modifiers the controller names are the only ones
|
||||
/// that survive. Without the mask a right-drag would arrive at the controller
|
||||
/// with an extra `0x400` bit that no other adapter's event carries.
|
||||
inline u32 mods_from(u32 gdk_state) noexcept {
|
||||
// The GDK bit values are repeated rather than included: this header names no
|
||||
// GDK type, and the constants are fixed by GDK's public ABI. `gtk_plot_area.cpp`
|
||||
// static-asserts them against GDK's own enumerators.
|
||||
constexpr u32 kGdkShift = 1u << 0;
|
||||
constexpr u32 kGdkControl = 1u << 2;
|
||||
constexpr u32 kGdkAlt = 1u << 3;
|
||||
constexpr u32 kGdkSuper = 1u << 26;
|
||||
constexpr u32 kGdkMeta = 1u << 28; // maps to Command on macOS, Super here
|
||||
|
||||
u32 out = 0;
|
||||
if ((gdk_state & kGdkShift) != 0u) out |= static_cast<u32>(input::Mod::Shift);
|
||||
if ((gdk_state & kGdkControl) != 0u) out |= static_cast<u32>(input::Mod::Ctrl);
|
||||
if ((gdk_state & kGdkAlt) != 0u) out |= static_cast<u32>(input::Mod::Alt);
|
||||
if ((gdk_state & kGdkSuper) != 0u || (gdk_state & kGdkMeta) != 0u) {
|
||||
out |= static_cast<u32>(input::Mod::Super);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// The button one of GTK's gestures reports, as the controller's enum.
|
||||
///
|
||||
/// Buttons 1, 2 and 3 are the only ones GTK names (left, middle, right) and the
|
||||
/// only ones the controller has a gesture for. This adapter listens with
|
||||
/// `gtk_gesture_single_set_button(gesture, 0)`, which GTK documents as "any
|
||||
/// button", so a press from button 8 or 9 (the navigation buttons) reaches the
|
||||
/// handler too: `nullopt` is returned for it, and the event is dropped before it
|
||||
/// can begin a pan.
|
||||
inline std::optional<input::Button> button_from(u32 gdk_button) noexcept {
|
||||
switch (gdk_button) {
|
||||
case 1u: return input::Button::Left;
|
||||
case 2u: return input::Button::Middle;
|
||||
case 3u: return input::Button::Right;
|
||||
default: return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
/// A GDK scroll delta in wheel notches, positive = zoom in.
|
||||
///
|
||||
/// Two conversions in one place, the same two every adapter has to make:
|
||||
///
|
||||
/// * the *unit* -- a `ScrollUnit::Surface` delta is scaled by
|
||||
/// `1 / kSurfacePixelsPerNotch` (see there); a `ScrollUnit::Wheel` delta is
|
||||
/// already one unit per detent;
|
||||
/// * the *sign* -- GDK's positive `dy` is a scroll south, the wheel pushed away
|
||||
/// from the user, while the controller's positive amount zooms in, so the
|
||||
/// value is negated. A wheel notch up arrives as `dy == -1.0` and leaves here
|
||||
/// as `+1.0`.
|
||||
///
|
||||
/// A non-finite delta is returned unchanged: `Controller::on_wheel` refuses it as
|
||||
/// a strict no-op, which is the one place that decision belongs.
|
||||
inline f64 scroll_notches(f64 dy, ScrollUnit unit) noexcept {
|
||||
if (unit == ScrollUnit::Surface) return -dy / kSurfacePixelsPerNotch;
|
||||
return -dy;
|
||||
}
|
||||
|
||||
} // namespace plot::gtk4
|
||||
Reference in new issue
Block a user