Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
widgets.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <cstddef>
7#include <cstdint>
8#include <functional>
9#include <memory>
10#include <optional>
12#include <pulp/canvas/text_shaper.hpp> // canvas::ShapedLayout for Label's shaped-layout cache
18#include <pulp/view/caret.hpp>
26#include <pulp/view/view.hpp>
28#include <string>
29#include <string_view>
30#include <vector>
31
32namespace pulp::view {
33
34class TextEditor; // pulp/view/text_editor.hpp — the Label's inline edit field
35
36// ── Label ────────────────────────────────────────────────────────────────────
37// Static or dynamic text display
38
55
56class Label : public View, public SelectableText {
57 public:
59 explicit Label(std::string text) : text_(std::move(text)) {
62 }
63
64 // Changes the copy. Requests paint only when no layout output can depend
65 // on it; otherwise invalidates layout as well.
66 void set_text(std::string text);
67 const std::string& text() const { return text_; }
68
69 // Per-range styled text (design-import mixed text). Every line shape and
70 // paint path retains the spans; captured lines and responsive reflow share
71 // the same attributed renderer.
73 attributed_runs_ = std::move(a);
74 has_attributed_ = !attributed_runs_.spans().empty();
75 // A run mutation changes advances independently of the Label-wide
76 // typography captured in set_cached_line_boxes(). Callers that want to
77 // install both must set the runs first and the captured basis second.
78 cached_line_boxes_.clear();
79 shaped_cache_valid_ = false;
81 }
83 has_attributed_ = false;
84 cached_line_boxes_.clear();
85 shaped_cache_valid_ = false;
87 }
88 bool has_attributed_string() const { return has_attributed_; }
89 std::size_t attributed_span_count() const { return attributed_runs_.spans().size(); }
90
91 // Each setter marks the corresponding has_own_* flag so
92 // paint() can distinguish "default value" from "explicitly set" and
93 // fall through to inheritable_*() for unset properties.
94 // Typography that feeds the shaper's cache key — size, family, weight,
95 // style, letter spacing, line height, wrap mode, case transform — decides
96 // this Label's measured box, so changing it is a geometry mutation and has
97 // to advance the layout generation. Both layout gates
98 // (View::layout_children_if_needed and WidgetBridge::ensure_layout) elide
99 // the pass while the applied generation matches the tree's, so a setter
100 // that stays silent here leaves the Label painting at the box its PREVIOUS
101 // typography solved to, and leaves every ancestor sized for it.
102 //
103 // Each setter is guarded on an actual change. A materialized React commit
104 // replays an element's complete style object when only paint state moved;
105 // dirtying the tree for an identical write would make every following
106 // geometry read pay for a full Yoga pass, which is the same contract
107 // flex_styles_equal keeps on the flex path.
108 void set_font_size(float size) {
109 if (has_own_font_size_ && font_size_ == size)
110 return;
111 font_size_ = size;
112 has_own_font_size_ = true;
114 }
115 float font_size() const { return font_size_; }
116 bool has_own_font_size() const { return has_own_font_size_; }
117
120 void set_font_family(std::string family) {
121 if (font_family_ == family)
122 return;
123 font_family_ = std::move(family);
125 }
126 const std::string& font_family() const { return font_family_; }
127
128 // 100-900, 400=normal, 700=bold
129 void set_font_weight(int weight) {
130 if (has_own_font_weight_ && font_weight_ == weight)
131 return;
132 font_weight_ = weight;
133 has_own_font_weight_ = true;
135 }
136 int font_weight() const { return font_weight_; }
137 bool has_own_font_weight() const { return has_own_font_weight_; }
138
139 // 0=normal, 1=italic, 2=oblique
140 void set_font_style(int style) {
141 if (font_style_ == style)
142 return;
143 font_style_ = style;
145 }
146 int font_style() const { return font_style_; }
147
148 void set_letter_spacing(float sp) {
149 if (has_own_letter_spacing_ && letter_spacing_ == sp)
150 return;
151 letter_spacing_ = sp;
152 has_own_letter_spacing_ = true;
154 }
155 float letter_spacing() const { return letter_spacing_; }
156 bool has_own_letter_spacing() const { return has_own_letter_spacing_; }
157
161 if (font_smoothing_ == smoothing)
162 return;
163 font_smoothing_ = smoothing;
165 }
167 return font_smoothing_;
168 }
169
170 void set_line_height(float lh) {
171 if (line_height_ == lh)
172 return;
173 line_height_ = lh;
175 }
176 float line_height() const { return line_height_; }
177
178 void set_text_align(LabelAlign align) { text_align_ = align; has_own_text_align_ = true; }
179 LabelAlign text_align() const { return text_align_; }
180 bool has_own_text_align() const { return has_own_text_align_; }
181
182 // Explicit text color override on the Label itself. When
183 // set, overrides any inherited typography color and the theme token.
184 void set_text_color(canvas::Color c) { text_color_ = c; has_own_text_color_ = true; }
185 bool has_own_text_color() const { return has_own_text_color_; }
186 canvas::Color text_color() const { return text_color_; }
187
188 void set_multi_line(bool ml) {
189 if (multi_line_ == ml)
190 return;
191 multi_line_ = ml;
193 }
194 bool multi_line() const { return multi_line_; }
195
208 bool soft_wraps() const { return multi_line_ && !inherits_nowrap(); }
211 bool inherits_nowrap() const;
212
222 void set_line_clamp(int n) { line_clamp_ = (n < 0) ? 0 : n; }
223 int line_clamp() const { return line_clamp_; }
224
228 if (text_transform_ == t)
229 return;
230 text_transform_ = t;
232 }
233 TextTransform text_transform() const { return text_transform_; }
234
237 void set_text_decoration(TextDecoration d) { text_decoration_ = d; }
238 TextDecoration text_decoration() const { return text_decoration_; }
239 void set_text_decoration_color(canvas::Color c) { decoration_color_ = c; has_decoration_color_ = true; }
240 canvas::Color text_decoration_color() const { return decoration_color_; }
241 bool has_text_decoration_color() const { return has_decoration_color_; }
242
250 void set_text_decoration_style(TextDecorationStyle s) { text_decoration_style_ = s; }
251 TextDecorationStyle text_decoration_style() const { return text_decoration_style_; }
252
253 void paint(canvas::Canvas& canvas) override;
254
261 Rect own_text_box() const { return own_text_box_; }
262 bool has_own_text_box() const { return own_text_box_.width >= 0.0f; }
263 void set_own_text_box(Rect box) { own_text_box_ = box; }
264 void clear_own_text_box() { own_text_box_ = {0.0f, 0.0f, -1.0f, -1.0f}; }
265
273 float intrinsic_width() const override;
274
283 float max_content_width() const;
284
292 float intrinsic_height() const override;
293
302 float measured_height(float available_width) const;
303
315 float measured_width(float available_width) const;
316
335 bool measured = false;
336 float width = 0.0f;
337 float height = 0.0f;
338 int line_count = 0;
344 };
345 PaintedTextExtents painted_text_extents(float available_width) const;
346
350 float left = 0.0f;
351 float top = 0.0f;
352 float width = 0.0f;
353 float height = 0.0f;
354 int start = 0;
355 int length = 0;
356 };
357
367 int cached = 0;
368 int reflowed = 0;
369 int uncached = 0;
370 };
373
388 void set_cached_line_boxes(std::vector<CachedLineBox> boxes,
389 float basis_width, std::string basis_face,
390 bool wrap_on_cache_miss = false);
395 cached_line_boxes_.clear();
396 cached_line_metrics_.reset();
397 shaped_cache_valid_ = false;
399 }
400
406 const std::vector<CachedLineBox>& cached_line_boxes() const {
407 return cached_line_boxes_;
408 }
409 bool captured_wrap_fallback() const { return captured_wrap_fallback_; }
410
421
432 std::string effective_font_family() const;
433
441 float baseline_y() const;
442
459 std::string display_text;
460 float local_text_left = 0.0f;
461 float local_band_y = 0.0f;
462 float band_height = 0.0f;
463 std::vector<float> caret_x_by_byte;
464 };
466 std::string_view edit_text) const;
467
468 // ── Click-to-edit ────────────────────────────────────────────────────
469 //
470 // A Label can turn itself into a text field in place. `set_editable`
471 // arms the gesture; a matching click spawns a child TextEditor sized to
472 // the Label's bounds, seeded with the Label's text, focused and
473 // select-all'd. Return commits (the Label's text is replaced and
474 // `on_text_change` fires), Escape reverts, and clicking away commits —
475 // the three endings a rename field is expected to have.
476 //
477 // The editor is a real child View, so a painter or metrics delegate
478 // installed on an ancestor styles it exactly like any other text field.
479
485
486 void set_editable(EditTrigger trigger) {
487 edit_trigger_ = trigger;
489 }
490 EditTrigger editable() const { return edit_trigger_; }
491
494 std::function<void(const std::string& text)> on_text_change;
495
498 std::function<void()> on_editor_show;
499 std::function<void()> on_editor_hide;
500
506 void hide_editor(bool commit);
507 bool editor_active() const { return editor_ != nullptr; }
510 TextEditor* editor() const { return editor_; }
511
512 void on_mouse_down(Point pos) override;
513 bool wants_mouse_input() const override { return edit_trigger_ != EditTrigger::none; }
514
515 // ── SelectableText ──────────────────────────────────────────────────
516 // A Label joins a document-level selection (pulp/view/selectable_text.hpp)
517 // using the line geometry its own paint already computes. The geometry is
518 // recorded during paint rather than re-derived, so the band a selection
519 // draws sits on the glyphs that were actually emitted — including after
520 // text-transform, soft wrap, line-clamp and ellipsis truncation, none of
521 // which a re-measurement from `text_` would reproduce.
522
524 return is_selectable() ? this : nullptr;
525 }
526
541 if (selection_policy_ == policy)
542 return;
543 selection_policy_ = policy;
544 if (!is_selectable())
547 }
549 return selection_policy_;
550 }
551
561 bool is_selectable() const {
562 if (selection_policy_ == SelectionPolicy::always)
563 return true;
564 if (selection_policy_ == SelectionPolicy::never)
565 return false;
566 return enclosing_text_selection_region(*const_cast<Label*>(this)) != nullptr;
567 }
568
569 std::string_view selectable_text() const override {
570 return text_;
571 }
573 return selection_layout_;
574 }
575 void set_selection_highlight(int start_utf8, int end_utf8) override;
576 bool selection_highlight(int& start_utf8, int& end_utf8) const override;
577 View* selectable_view() override {
578 return this;
579 }
580
581private:
582 SelectionPolicy selection_policy_ = SelectionPolicy::inherit;
583 int selection_start_utf8_ = 0;
584 int selection_end_utf8_ = 0;
589 mutable SelectableLayout selection_layout_;
590 mutable SelectableLayout selection_layout_pending_;
591
600 void paint_selection_line_(canvas::Canvas& canvas, const std::string& line, float x, float top,
601 float line_height, int source_start, const canvas::Color& text_color,
602 bool vertical);
603
604 EditTrigger edit_trigger_ = EditTrigger::none;
605 TextEditor* editor_ = nullptr;
606 int click_count_ = 0;
607 double last_click_time_ = 0.0;
608
616 struct ResolvedTextStyle {
617 std::string family;
618 float font_size = 14.0f;
619 int font_weight = 400;
620 int font_slant = 0;
621 float letter_spacing = 0.0f;
623 float baseline_y = 0.0f;
624 };
625 ResolvedTextStyle resolve_text_style() const;
626 std::string apply_text_transform(const std::string& in) const;
632 std::vector<canvas::Canvas::FontFeature> resolved_font_features() const;
633 void apply_font_features(canvas::Canvas& canvas) const;
634 canvas::AttributedString resolved_attributed_string() const;
635
636 std::string text_;
639 Rect own_text_box_{0.0f, 0.0f, -1.0f, -1.0f};
640 std::string font_family_;
641 float font_size_ = 14.0f;
642 int font_weight_ = 400;
643 int font_style_ = 0;
644 float letter_spacing_ = 0;
646 float line_height_ = 0;
647 LabelAlign text_align_ = LabelAlign::left;
648 bool multi_line_ = false;
649 int line_clamp_ = 0;
650 TextTransform text_transform_ = TextTransform::none;
651 TextDecoration text_decoration_ = TextDecoration::none;
652 canvas::Color decoration_color_{};
653 bool has_decoration_color_ = false;
654 TextDecorationStyle text_decoration_style_ = TextDecorationStyle::solid;
657 // Explicit-vs-inherited tracking. Fields keep their default
658 // values until a setter is called; the has_own_* flag tells paint()
659 // whether to honor the field or fall through to View::inheritable_*().
660 canvas::Color text_color_{};
661 bool has_own_text_color_ = false;
662 canvas::AttributedString attributed_runs_;
663 bool has_attributed_ = false;
667 void paint_text_(canvas::Canvas& canvas, Rect text_box);
668
669 void paint_decoration_(canvas::Canvas& canvas, float x, float width,
670 float baseline, float font_size,
671 const canvas::Color& color);
672 void paint_attributed_lines_(canvas::Canvas& canvas,
673 const canvas::ShapedLayout& layout,
674 float baseline_y, float line_height,
675 int visible_lines, bool append_ellipsis,
676 bool single_line_ellipsis,
677 bool captured_positions);
679 resolve_effective_align_() const;
680 bool has_own_font_size_ = false;
681 bool has_own_font_weight_ = false;
682 bool has_own_letter_spacing_ = false;
683 bool has_own_text_align_ = false;
684
685 // Cache of the soft-wrap shaped layout so paint() reuses it instead of
686 // re-running the expensive TextShaper prepare()+layout each frame. Keyed on
687 // every input the shaper reads; a key mismatch recomputes, so no stale hit
688 // is possible.
689 //
690 // Weight IS one of those inputs. A Bold face is a different set of glyph
691 // advances, not a Regular one drawn heavier, so the same string at the same
692 // size in the same family breaks at a different word depending on it —
693 // which is why a key without it serves a Regular layout to a Bold label.
694 struct ShapedLayoutKey {
695 std::string display_text; // text_ after text-transform
696 std::string family; // resolved family ("Inter" fallback)
697 std::string font_variant;
698 float font_size = 0.0f;
699 int font_weight = 400; // CSS numeric weight; selects the face
700 int font_style = 0;
701 float letter_spacing = 0.0f;
702 float width = 0.0f; // bounds().width — changes every resize
703 float line_height = 0.0f;
704 int break_mode = 0; // canvas::BreakMode as int
705 int max_lines = 0; // attributed nowrap uses one; wrap is unlimited
706 // Captured browser line positions and native reflow can have identical
707 // shaping inputs but different x/y origins. Eligibility may change as
708 // imported ancestor styles settle, so it is part of the cache key,
709 // not merely a branch taken on cache construction.
710 bool captured_positions = false;
711 std::uint64_t font_gen = 0; // font_registration_generation() snapshot
712 bool operator==(const ShapedLayoutKey& o) const {
713 return display_text == o.display_text && family == o.family &&
714 font_variant == o.font_variant &&
715 font_size == o.font_size && font_weight == o.font_weight &&
716 font_style == o.font_style &&
717 letter_spacing == o.letter_spacing &&
718 width == o.width && line_height == o.line_height &&
719 break_mode == o.break_mode && max_lines == o.max_lines &&
720 captured_positions == o.captured_positions &&
721 font_gen == o.font_gen;
722 }
723 };
731 struct ShapingSetup {
732 std::string display_text;
733 float line_height = 0.0f;
734 float font_size = 0.0f;
735 float letter_spacing = 0.0f;
736 float shaping_line_height = 0.0f;
738 };
739 ShapingSetup shaping_setup() const;
740
744 canvas::PreparedText prepare_shaped(const ShapingSetup& setup) const;
745
748 canvas::ShapedLayout shaper_layout_for(const canvas::PreparedText& prepared,
749 const ShapingSetup& setup,
750 float available_width) const;
751
758 bool cached_line_layout_usable(const std::string& display_text,
759 float effective_font_size,
760 float effective_letter_spacing,
761 float layout_width) const;
762
771 canvas::ShapedLayout layout_from_cached_lines(const std::string& text, float line_height,
772 const std::string& family,
773 float effective_font_size,
774 float effective_letter_spacing) const;
775 canvas::ShapedLayout layout_attributed_from_cached_lines(
776 const canvas::AttributedString& text, float line_height) const;
777
778 std::vector<CachedLineBox> cached_line_boxes_;
779 bool captured_wrap_fallback_ = false;
780 // Measured-size memo. See Label::sync_measure_basis in label.cpp for why
781 // the basis is the resolved style and why the face is keyed through the
782 // font-registration generation rather than resolved per call.
783 struct MeasureBasis {
784 std::string text;
785 std::string family;
786 std::string font_variant;
787 float font_size = 0.0f;
788 float letter_spacing = 0.0f;
789 int font_weight = 400;
790 int font_style = 0;
791 int text_direction = 0;
792 int line_clamp = 0;
793 bool multi_line = false;
794 bool wrap_fallback = false;
795 std::uint64_t font_gen = 0;
796 // Every input shaping_setup()/prepare_shaped() reads must appear
797 // here, or a restyle that changes only one of them keeps the stale
798 // box: line height scales measured height directly, a text-transform
799 // changes advances and so can change the line count, the word-break
800 // mode changes where lines break, and font features change shaping.
801 float line_height = 0.0f;
802 int text_transform = 0;
803 std::string word_break;
804 std::vector<canvas::Canvas::FontFeature> font_features;
805 bool operator==(const MeasureBasis&) const = default;
806 };
809 bool sync_measure_basis() const;
810 float compute_intrinsic_width() const;
811 float compute_max_content_width() const;
819 bool paints_as_lines() const { return soft_wraps() || captured_wrap_fallback_; }
820 float compute_intrinsic_height() const;
821 float compute_measured_height(float available_width) const;
822 mutable MeasureBasis measure_basis_;
823 mutable bool measure_basis_valid_ = false;
824 mutable float measure_width_ = -1.0f;
825 mutable float measure_height_ = -1.0f;
826 mutable float measure_wrapped_for_width_ = -1.0f;
827 mutable float measure_wrapped_height_ = -1.0f;
828
829 float cached_line_basis_width_ = 0.0f;
830 std::optional<LineMetrics> cached_line_metrics_;
831 std::string cached_line_basis_face_;
832 std::string cached_line_basis_text_;
833 // Set once the label's copy diverged from the captured basis; the boxes
834 // are kept for reflow but never honored verbatim again.
835 bool cached_line_basis_retired_ = false;
836 std::string cached_line_basis_font_variant_;
837 float cached_line_basis_font_size_ = 0.0f;
838 float cached_line_basis_letter_spacing_ = 0.0f;
839 int cached_line_basis_font_weight_ = 400;
840 int cached_line_basis_font_style_ = 0;
841
842 ShapedLayoutKey shaped_cache_key_;
843 canvas::ShapedLayout shaped_cache_layout_;
844 bool shaped_cache_valid_ = false;
845 // The shaped-layout cache cannot prevent work performed before its lookup.
846 // Retain the expensive attributed preparation separately so an unchanged
847 // paint performs no shaping even though it still resolves inherited spans.
848 canvas::AttributedString attributed_prepare_source_;
849 std::vector<canvas::Canvas::FontFeature> attributed_prepare_features_;
850 canvas::PreparedText attributed_prepare_cache_;
851 std::uint64_t attributed_prepare_font_gen_ = 0;
852 bool attributed_prepare_valid_ = false;
853
868 struct FaceMetrics {
869 float ascent = 0.0f;
870 float descent = 0.0f;
871 // fDescent; see PreparedText::typographic_descent().
872 float typographic_descent = 0.0f;
873 bool real = false;
874 };
875 FaceMetrics face_metrics(const std::string& family, float font_size,
876 int font_weight, int font_style) const;
877 mutable std::string face_metrics_family_;
878 mutable float face_metrics_font_size_ = 0.0f;
879 mutable int face_metrics_font_weight_ = 400;
880 mutable int face_metrics_font_style_ = 0;
881 mutable std::uint64_t face_metrics_font_gen_ = 0;
882 mutable FaceMetrics face_metrics_cache_;
883 mutable bool face_metrics_valid_ = false;
884public:
886 void set_text_direction(canvas::TextDirection d) { text_direction_ = d; }
887 canvas::TextDirection text_direction() const { return text_direction_; }
888
890 void set_vertical_align(canvas::TextVerticalAlign a) { vertical_align_ = a; }
891 canvas::TextVerticalAlign vertical_align() const { return vertical_align_; }
892};
893
894// ── Knob ─────────────────────────────────────────────────────────────────────
895// Rotary control for audio parameters (gain, frequency, etc.)
896
897// AccessibilityValueInterface: `AccessRole::slider` advertises the UIA
898// RangeValue/Value patterns and the AT-SPI Value interface. A slider with no
899// value source announces its name and then reads nothing (macOS) or reports
900// 0 in a degenerate 0..0 range (Windows). The role and the value ship together.
902public:
906 set_focusable(true);
907 // A knob's pointer travel is shorter than a fader's: 150px crosses the
908 // whole range, which is what a compact plugin panel expects.
909 core_.set_drag_sensitivity(150);
910 }
911
912 // ── Value engine ─────────────────────────────────────────────────────
913 //
914 // The knob's REAL-WORLD value, range, interval, response curve and drag law
915 // live in a SliderCore (slider_core.hpp). `value()` below stays the
916 // normalized [0,1] position in that range — that is the space a parameter
917 // store and the design/JS bridges speak — so a knob with the default 0..1
918 // range behaves exactly as it always has, and `set_range()` is what turns it
919 // into a real control (20..20000 Hz, -60..+12 dB, quantized to an interval).
920
921 SliderCore& slider() { return core_; }
922 const SliderCore& slider() const { return core_; }
923
926 void set_range(double min_value, double max_value, double interval = 0.0) {
927 core_.set_range(min_value, max_value, interval);
928 core_.set_normalized(value_, Notify::none);
929 value_ = static_cast<float>(core_.normalized());
931 }
932 double real_value() const { return core_.value(); }
934 bool set_real_value(double v, Notify notify = Notify::sync) {
935 core_.set_value(v, Notify::none);
936 return store_normalized(static_cast<float>(core_.normalized()), notify);
937 }
938
939 // ── AccessibilityValueInterface (normalized 0..1, like value()) ──────
940 double get_current_value() const override { return value_; }
941 void set_current_value(double v) override {
942 const float prev = value_;
943 set_value(static_cast<float>(v));
944 if (value_ != prev && on_change) on_change(value_);
945 }
946 double get_minimum_value() const override { return 0.0; }
947 double get_maximum_value() const override { return 1.0; }
950 std::string get_value_string() const override {
951 return format_ ? format_(value_)
953 }
954
955 void set_render_style(WidgetRenderStyle s) { render_style_ = s; }
956 WidgetRenderStyle render_style() const { return render_style_; }
957
958 // Programmatic set_value() must request_repaint() when the value actually
959 // changes. The host's mouseDown handler invalidates after every input event,
960 // so user drags repaint correctly via that side channel; preset application
961 // and other JS-driven bridge mutations go through this code path and would
962 // otherwise leave the painted state stale until the next user input.
963 //
964 // Gate the invalidation on a real change. WidgetBridge::sync_from_store()
965 // and restore_values(...) call set_value()/set_on() in tight loops during
966 // sync/reload; firing a host repaint per call when the value is unchanged
967 // burns wall-clock on large widget trees with no visible benefit.
968 void set_value(float v) { store_normalized(v, Notify::none); }
969
975 bool set_value(float v, Notify notify) { return store_normalized(v, notify); }
976
977 float value() const { return value_; }
978
979 void set_default_value(float v) {
980 default_value_ = std::clamp(v, 0.0f, 1.0f);
981 core_.set_default_value(core_.value_for_normalized(default_value_));
982 }
983 float default_value() const { return default_value_; }
984
990 void set_skew(float s) { core_.set_skew(s); }
991 float skew() const { return static_cast<float>(core_.skew()); }
994 void set_skew_from_midpoint(float mid_normalized) {
995 core_.set_skew_from_midpoint(
996 core_.value_for_normalized(std::clamp(mid_normalized, 1e-4f, 1.0f - 1e-4f)));
997 }
998 float position_for_value() const {
999 const float s = skew();
1000 return s == 1.0f ? value_ : std::pow(value_, s);
1001 }
1002 float value_for_position(float p) const {
1003 p = std::clamp(p, 0.0f, 1.0f);
1004 const float s = skew();
1005 return s == 1.0f ? p : std::pow(p, 1.0f / s);
1006 }
1007
1008 void set_label(std::string text) {
1009 if (label_ == text) return;
1010 label_ = std::move(text);
1011 set_derived_access_label(label_); // the visible label is the accessible name
1013 }
1014 const std::string& label() const { return label_; }
1015
1016 void set_show_label(bool show) {
1017 if (show_label_ == show) return;
1018 show_label_ = show;
1020 }
1021 bool show_label() const { return show_label_; }
1022
1026 void set_show_value(bool show) {
1027 if (show_value_ == show) return;
1028 show_value_ = show;
1030 }
1031 bool show_value() const { return show_value_; }
1032
1033 // Display format: called with normalized value to produce display text.
1034 // No equality check (std::function doesn't compare) — formatters are
1035 // set at construction or theme-change time, not in tight sync loops.
1036 void set_format(std::function<std::string(float)> fmt) {
1037 format_ = std::move(fmt);
1039 }
1040
1042 std::function<void(float)> on_change;
1043 std::function<void()> on_gesture_begin;
1044 std::function<void()> on_gesture_end;
1045
1046 void paint(canvas::Canvas& canvas) override;
1047 void on_mouse_event(const MouseEvent& event) override;
1048 void on_mouse_enter() override;
1049 void on_mouse_leave() override;
1050 void on_mouse_down(Point pos) override;
1051 void on_mouse_up(Point pos) override;
1052 void on_mouse_drag(Point pos) override;
1053 bool wants_mouse_input() const override { return true; }
1054
1055 // Animation accessors for testing
1056 float hover_glow() const { return hover_glow_.value(); }
1057 void advance_animations(float dt) override;
1058
1059 // Arc range in radians (default: 270-degree sweep)
1060 static constexpr float start_angle = 2.356f; // 135 degrees (bottom-left)
1061 static constexpr float end_angle = 7.069f; // 405 degrees (bottom-right via top)
1062
1063 // Custom body shader comes from CustomShaderHost — it replaces the
1064 // body/track/fill arc drawing; labels, value text, and hover glow still
1065 // draw in C++.
1066
1067 // Declarative widget schema: JSON defining appearance as data (Rive-inspired)
1068 void set_widget_schema(std::string json) { widget_schema_ = std::move(json); }
1069 const std::string& widget_schema() const { return widget_schema_; }
1070
1071 // ── Modulation rings (Saturn) ───────────────────────────────────────
1072 // Thin concentric rings drawn OUTSIDE the value arc, one per modulation
1073 // source. Matches the Figma "Knob Modulation" set (LFO / ENV / VEL / MACRO).
1074 // Empty by default (a plain knob).
1076 // Two INDEPENDENT signed offsets from the base value, one per arc end —
1077 // dragging one handle moves only that end (not a symmetric grow/shrink
1078 // off a single depth). Unipolar-positive: lo=0, hi>0. Unipolar-negative:
1079 // lo<0, hi=0. Bipolar: lo<0, hi>0. The range is [base+lo, base+hi]; the
1080 // endpoints sort themselves, so the two may cross without breaking paint.
1081 float lo = 0.0f;
1082 float hi = 0.0f;
1084 };
1085 void set_modulation_rings(std::vector<ModulationRing> rings) {
1086 mod_rings_ = std::move(rings);
1088 }
1089 const std::vector<ModulationRing>& modulation_rings() const { return mod_rings_; }
1090
1091 // Live modulation phase in [-1, 1] — the instantaneous output of the
1092 // assigned source(s). Drives the indicator dot that rides the modulation
1093 // arc, showing where the parameter actually IS right now. Set it from a
1094 // master/macro control or animate it from the host (e.g. an LFO).
1096 float c = std::clamp(p, -1.0f, 1.0f);
1097 // Also a hot path — a host/LFO can drive this every frame.
1098 if (c != mod_phase_) { mod_phase_ = c; request_repaint_self(); }
1099 }
1100 float modulation_phase() const { return mod_phase_; }
1101
1102 // ── Modulated value display ─────────────────────────────────────────
1103 //
1104 // The value a processor is PLAYING while a modulator (an internal LFO, an
1105 // envelope, MPE) moves this parameter around the value the user set.
1106 // Display only, and the contract every modulated control keeps:
1107 //
1108 // * value() stays the BASE. A drag, a wheel notch or a key starts from
1109 // it, and on_change / gestures report it, so
1110 // host automation records the user's moves and nothing else.
1111 // * set_modulated_value() never fires on_change and never writes a
1112 // parameter: modulation must not write a host automation lane. It
1113 // offsets around the automated (or user-set) base, so a control host
1114 // automation is moving shows both -- the base following the lane and
1115 // the modulated marker following the modulator around it.
1116 // * The marker repaints the knob's own box (request_repaint_self), never
1117 // the whole surface and never the layout, so a modulator animating a
1118 // control at the display rate costs one bounded repaint per frame.
1119 //
1120 // One indicator, never two: while a played value is set, the knob's single
1121 // pointer and value arc show the value PLAYING, in the modulation colour
1122 // (token `knob.modulation`), on every body style; the base is a quiet
1123 // notch across the ring. The value text prints the played value, and the
1124 // base while the user drags it (the drag edits the base and the notch
1125 // follows).
1126 // Values are normalized 0..1, like value(). Unlike the Saturn rings above,
1127 // which DERIVE the live value from base + depth x phase, this takes the
1128 // value the processor computed, for modulation laws that are not a linear
1129 // offset (a proportional pull, a clamp, a stepped target).
1130 void set_modulated_value(float normalized) {
1131 const float v = std::isfinite(normalized) ? std::clamp(normalized, 0.0f, 1.0f) : value_;
1132 if (has_modulated_ && v == modulated_)
1133 return;
1134 has_modulated_ = true;
1135 modulated_ = v;
1136 request_repaint_self(kModulatedMarkerHalo);
1137 }
1139 if (!has_modulated_)
1140 return;
1141 has_modulated_ = false;
1142 request_repaint_self(kModulatedMarkerHalo);
1143 }
1144 bool has_modulated_value() const {
1145 return has_modulated_;
1146 }
1147
1149 return has_modulated_ ? modulated_ : value_;
1150 }
1151
1152 // The modulation range is [value+lo, value+hi] clipped to [0,1], sorted so
1153 // the returned .first ≤ .second regardless of which end is which. Exposes the
1154 // clipped endpoints of ring i for tests / hosts. Returns {lo_v, hi_v}.
1155 std::pair<float, float> modulation_range(size_t ring) const;
1156 // The live modulated value of ring i: phase≥0 sweeps toward base+hi, phase<0
1157 // toward base+lo, clipped to [0,1].
1158 float modulated_value(size_t ring) const;
1159
1160 // Fired when dragging a modulation-arc handle changes a ring's endpoints.
1161 // Reports BOTH offsets (the dragged one moved, the other is unchanged).
1162 std::function<void(int ring, float lo, float hi)> on_modulation_change;
1163 // True while a modulation-arc handle is being dragged (vs the value).
1164 bool dragging_modulation() const { return mod_drag_ring_ >= 0; }
1165
1166 // Scroll-wheel adjusts the value (hover + wheel).
1167 bool wants_wheel_value() const override { return true; }
1168 void on_wheel(float delta_y) override {
1169 // A QUANTIZED control moves by exactly one interval per notch. The
1170 // continuous nudge below is smaller than most intervals, so snapping
1171 // would return the value to the step it started on and the wheel would
1172 // do nothing at all — a control with an interval had a dead wheel.
1173 if (core_.interval() > 0.0) {
1174 const double step = (delta_y < 0.0f ? 1.0 : -1.0) * core_.interval();
1175 set_real_value(core_.value() + step, Notify::sync);
1176 return;
1177 }
1178 store_normalized(value_ + (-delta_y) * 0.004f, Notify::sync);
1179 }
1180
1181 // True while a value drag (or modulation-handle drag) is in progress so a
1182 // declarative param binding yields the value to the user mid-turn.
1183 bool is_gesture_active() const override { return gesture_active_ || mod_drag_ring_ >= 0; }
1184
1185private:
1187 static constexpr float kModulatedMarkerHalo = 6.0f;
1188 void paint_modulated_marker(canvas::Canvas& canvas, float cx, float cy, float ring_r,
1189 float arc_w);
1190 bool has_modulated_ = false;
1191 float modulated_ = 0.0f;
1192 std::vector<ModulationRing> mod_rings_;
1193 float mod_phase_ = 0.0f;
1194 int mod_drag_ring_ = -1;
1195 bool mod_drag_is_high_ = true;
1196 float mod_drag_last_angle_ = 0.0f;
1200 float value_ = 0.0f;
1201 SliderCore core_;
1202 float default_value_ = 0.5f;
1203 std::string label_;
1204 std::function<std::string(float)> format_;
1205 ValueAnimation hover_glow_{0.0f};
1206 float drag_start_y_ = 0;
1207 float drag_start_proportion_ = 0;
1208 float drag_last_y_ = 0;
1209 bool gesture_active_ = false;
1210
1214
1215 bool store_normalized(float n, Notify notify) {
1216 core_.set_normalized(std::clamp(n, 0.0f, 1.0f), Notify::none);
1217 const float snapped = static_cast<float>(core_.normalized());
1218 if (snapped == value_)
1219 return false;
1220 value_ = snapped;
1222 if (notify == Notify::sync) {
1223 if (on_change)
1224 on_change(value_);
1225 } else if (notify == Notify::async && on_change) {
1226 auto fn = on_change;
1227 const float v = value_;
1228 queue_async_notification([fn, v] { fn(v); });
1229 }
1230 return true;
1231 }
1232 bool show_label_ = true;
1233 bool show_value_ = true;
1234 std::string widget_schema_; // JSON declarative schema
1235 std::string lottie_json_; // Lottie animation JSON
1237 float lottie_time_ = 0; // Current playback position (0-1)
1238
1239public:
1240 void set_lottie_json(std::string json) { lottie_json_ = std::move(json); }
1241 const std::string& lottie_json() const { return lottie_json_; }
1242 void set_lottie_time(float t) { lottie_time_ = std::clamp(t, 0.0f, 1.0f); }
1243 float lottie_time() const { return lottie_time_; }
1244
1247 void set_sprite_strip(std::shared_ptr<SpriteStrip> strip) { sprite_strip_ = std::move(strip); }
1248 const std::shared_ptr<SpriteStrip>& sprite_strip() const { return sprite_strip_; }
1249
1258 void set_sprite_core(float x, float y, float w, float h) {
1259 sprite_core_x_ = x; sprite_core_y_ = y;
1260 sprite_core_w_ = w; sprite_core_h_ = h;
1261 }
1262 bool has_sprite_core() const { return sprite_core_w_ > 0.0f && sprite_core_h_ > 0.0f; }
1263
1267 void set_captured_raster_origin(float x, float y, float scale) {
1268 captured_raster_origin_x_ = x;
1269 captured_raster_origin_y_ = y;
1270 captured_raster_scale_ = scale > 0.0f ? scale : 0.0f;
1271 }
1272 float captured_raster_scale() const { return captured_raster_scale_; }
1273 float captured_raster_origin_x() const { return captured_raster_origin_x_; }
1274 float captured_raster_origin_y() const { return captured_raster_origin_y_; }
1275
1285 void set_captured_indicator(float r_in, float r_out, float width_px,
1286 canvas::Color color, float phase_rad = 0.0f,
1287 bool color_authored = true) {
1288 ind_r_in_ = r_in; ind_r_out_ = r_out;
1289 ind_width_ = width_px; ind_color_ = color;
1290 ind_phase_rad_ = phase_rad;
1291 ind_color_authored_ = color_authored;
1292 has_captured_indicator_ = true;
1294 }
1295 bool has_captured_indicator() const { return has_captured_indicator_; }
1296 float captured_indicator_r_in() const { return ind_r_in_; }
1297 float captured_indicator_r_out() const { return ind_r_out_; }
1298 float captured_indicator_phase_rad() const { return ind_phase_rad_; }
1300 return ind_color_authored_;
1301 }
1302
1305 ind_outline_color_ = color;
1306 ind_outline_width_ = std::max(0.0f, width_px);
1308 }
1309 float captured_indicator_outline_width() const { return ind_outline_width_; }
1311 return ind_outline_color_;
1312 }
1313
1314private:
1315 std::shared_ptr<SpriteStrip> sprite_strip_;
1316 float sprite_core_x_ = 0.0f;
1317 float sprite_core_y_ = 0.0f;
1318 float sprite_core_w_ = 0.0f;
1319 float sprite_core_h_ = 0.0f;
1320 float captured_raster_origin_x_ = 0.0f;
1321 float captured_raster_origin_y_ = 0.0f;
1322 float captured_raster_scale_ = 0.0f;
1323 bool has_captured_indicator_ = false;
1324 float ind_r_in_ = 0.0f;
1325 float ind_r_out_ = 0.0f;
1326 float ind_width_ = 0.0f;
1327 float ind_phase_rad_ = 0.0f;
1328 canvas::Color ind_color_ = canvas::Color::rgba(1.0f, 1.0f, 1.0f, 1.0f);
1329 bool ind_color_authored_ = true;
1330 canvas::Color ind_outline_color_ = canvas::Color::rgba(0, 0, 0, 1.0f);
1331 float ind_outline_width_ = 0.0f;
1332};
1333
1334// ── Fader ────────────────────────────────────────────────────────────────────
1335// Linear slider for audio parameters
1336
1338public:
1340 enum class ThumbShape { circle, rectangle };
1341
1347
1348 // ── AccessibilityValueInterface (normalized 0..1) ────────────────────
1349 double get_current_value() const override { return value_; }
1350 void set_current_value(double v) override {
1351 const float prev = value_;
1352 set_value(static_cast<float>(v));
1353 if (value_ != prev && on_change) on_change(value_);
1354 }
1355 double get_minimum_value() const override { return 0.0; }
1356 double get_maximum_value() const override { return 1.0; }
1357
1358 void set_render_style(WidgetRenderStyle s) { render_style_ = s; }
1359 WidgetRenderStyle render_style() const { return render_style_; }
1360
1361 // Same programmatic repaint contract as Knob::set_value(), including the
1362 // no-change guard for bridge sync/reload loops. This form is silent (no
1363 // change callback), which is what a sync FROM the bound parameter wants.
1364 void set_value(float v) {
1365 float clamped = std::clamp(v, 0.0f, 1.0f);
1366 if (clamped == value_) return;
1367 value_ = clamped;
1369 }
1370
1378 bool set_value(float v, Notify notify) {
1379 float clamped = std::clamp(v, 0.0f, 1.0f);
1380 if (clamped == value_) return false;
1381 value_ = clamped;
1383 if (notify == Notify::sync) {
1384 if (on_change) on_change(value_);
1385 } else if (notify == Notify::async && on_change) {
1386 auto fn = on_change;
1387 const float nv = value_;
1388 queue_async_notification([fn, nv] { fn(nv); });
1389 }
1390 return true;
1391 }
1392 float value() const { return value_; }
1393
1400 void set_modulated_value(float normalized) {
1401 const float v = std::isfinite(normalized) ? std::clamp(normalized, 0.0f, 1.0f) : value_;
1402 if (has_modulated_ && v == modulated_)
1403 return;
1404 has_modulated_ = true;
1405 modulated_ = v;
1406 request_repaint_self(kModulatedMarkerHalo);
1407 }
1409 if (!has_modulated_)
1410 return;
1411 has_modulated_ = false;
1412 request_repaint_self(kModulatedMarkerHalo);
1413 }
1414 bool has_modulated_value() const {
1415 return has_modulated_;
1416 }
1418 return has_modulated_ ? modulated_ : value_;
1419 }
1420
1421 // Scroll-wheel adjusts the value (hover + wheel).
1422 bool wants_wheel_value() const override { return true; }
1423 void on_wheel(float delta_y) override {
1424 float nv = std::clamp(value_ + (-delta_y) * 0.004f, 0.0f, 1.0f);
1425 if (nv != value_) { set_value(nv); if (on_change) on_change(value_); }
1426 }
1427
1428 // True while the thumb is being dragged so a param binding yields mid-drag.
1429 bool is_gesture_active() const override { return dragging_; }
1430
1431 void set_orientation(Orientation o) { orientation_ = o; }
1432 Orientation orientation() const { return orientation_; }
1433
1435 if (thumb_shape_ == shape) return;
1436 thumb_shape_ = shape;
1438 }
1439 ThumbShape thumb_shape() const { return thumb_shape_; }
1440
1441 void set_thumb_size(float width, float height) {
1442 thumb_width_ = std::max(0.0f, width);
1443 thumb_height_ = std::max(0.0f, height);
1445 }
1446 float thumb_width() const { return thumb_width_; }
1447 float thumb_height() const { return thumb_height_; }
1448
1449 void set_thumb_corner_radius(float radius) {
1450 thumb_corner_radius_ = std::max(0.0f, radius);
1452 }
1453 float thumb_corner_radius() const { return thumb_corner_radius_; }
1454
1455 void set_label(std::string text) {
1456 if (label_ == text) return;
1457 label_ = std::move(text);
1458 set_derived_access_label(label_); // the visible label is the accessible name
1460 }
1461 const std::string& label() const { return label_; }
1462
1464 std::function<void(float)> on_change;
1465 std::function<void()> on_gesture_begin;
1466 std::function<void()> on_gesture_end;
1467
1468 void paint(canvas::Canvas& canvas) override;
1469 void on_mouse_enter() override;
1470 void on_mouse_leave() override;
1471 void on_mouse_down(Point pos) override;
1472 void on_mouse_up(Point pos) override;
1473 void on_mouse_event(const MouseEvent& event) override;
1474 void on_mouse_drag(Point pos) override;
1475 bool wants_mouse_input() const override { return true; }
1476
1477 // Animation accessors for testing
1478 float hover_scale() const { return hover_thumb_scale_.value(); }
1479 void advance_animations(float dt) override;
1480
1484 void set_skew(float s) { skew_ = std::max(0.0001f, s); }
1485 float skew() const { return skew_; }
1486 void set_skew_from_midpoint(float mid_normalized) {
1487 float p = std::clamp(mid_normalized, 1e-4f, 1.0f - 1e-4f);
1488 skew_ = std::max(0.0001f, std::log(0.5f) / std::log(p));
1489 }
1490
1492 float position_for_value() const { return skew_ == 1.0f ? value_ : std::pow(value_, skew_); }
1493 float value_for_position(float p) const {
1494 p = std::clamp(p, 0.0f, 1.0f);
1495 return skew_ == 1.0f ? p : std::pow(p, 1.0f / skew_);
1496 }
1497
1498 // Custom body shader comes from CustomShaderHost.
1499 void set_widget_schema(std::string json) { widget_schema_ = std::move(json); }
1500 const std::string& widget_schema() const { return widget_schema_; }
1501 void set_lottie_json(std::string json) { lottie_json_ = std::move(json); }
1502 const std::string& lottie_json() const { return lottie_json_; }
1503 void set_lottie_time(float t) { lottie_time_ = std::clamp(t, 0.0f, 1.0f); }
1504 float lottie_time() const { return lottie_time_; }
1505
1506
1507private:
1508 static constexpr float kModulatedMarkerHalo = 6.0f;
1509 void paint_modulated_marker(canvas::Canvas& canvas);
1510 // The stock thumb (slab or circle) at track position `pos`, 0..1.
1511 void paint_stock_thumb(canvas::Canvas& canvas, float pos, canvas::Color color);
1512 bool has_modulated_ = false;
1513 float modulated_ = 0.0f;
1514 float value_ = 0.0f;
1515 float skew_ = 1.0f;
1516 Orientation orientation_ = Orientation::vertical;
1517 // Ink & Signal faders use a slab/handle thumb by default (matches the Figma
1518 // design language); callers can opt back to a circle per-widget.
1519 ThumbShape thumb_shape_ = ThumbShape::rectangle;
1520 float thumb_width_ = 0.0f;
1521 float thumb_height_ = 0.0f;
1522 float thumb_corner_radius_ = 0.0f;
1523 bool has_skin_thumb_style_ = false;
1524 ThumbShape pre_skin_thumb_shape_ = ThumbShape::rectangle;
1525 float pre_skin_thumb_width_ = 0.0f;
1526 float pre_skin_thumb_height_ = 0.0f;
1527 float pre_skin_thumb_corner_radius_ = 0.0f;
1528 std::string label_;
1529 ValueAnimation hover_thumb_scale_{1.0f};
1530 bool dragging_ = false;
1531 std::string widget_schema_;
1532 std::string lottie_json_;
1533 float lottie_time_ = 0.0f;
1535
1536public:
1538 void set_sprite_strip(std::shared_ptr<SpriteStrip> strip) { sprite_strip_ = std::move(strip); }
1539 const std::shared_ptr<SpriteStrip>& sprite_strip() const { return sprite_strip_; }
1540
1546 void set_captured_art(std::shared_ptr<SpriteStrip> body,
1547 std::shared_ptr<SpriteStrip> indicator,
1548 float indicator_cross = 0.5f,
1549 float body_origin_x = 0.0f,
1550 float body_origin_y = 0.0f,
1551 float control_natural_w = 0.0f,
1552 float control_natural_h = 0.0f,
1553 bool body_includes_static_track = false) {
1554 captured_body_ = std::move(body);
1555 captured_indicator_ = std::move(indicator);
1556 captured_indicator_cross_ = std::clamp(indicator_cross, 0.0f, 1.0f);
1557 captured_body_origin_x_ = body_origin_x;
1558 captured_body_origin_y_ = body_origin_y;
1559 captured_control_natural_w_ = control_natural_w;
1560 captured_control_natural_h_ = control_natural_h;
1561 captured_body_includes_static_track_ = body_includes_static_track;
1563 }
1564 void set_captured_raster_origin(float x, float y, float scale) {
1565 captured_raster_origin_x_ = x;
1566 captured_raster_origin_y_ = y;
1567 captured_raster_scale_ = scale > 0.0f ? scale : 0.0f;
1568 }
1569 float captured_raster_scale() const { return captured_raster_scale_; }
1570 float captured_raster_origin_x() const { return captured_raster_origin_x_; }
1571 float captured_raster_origin_y() const { return captured_raster_origin_y_; }
1573 return captured_indicator_ && captured_indicator_->loaded();
1574 }
1575 float captured_indicator_cross() const { return captured_indicator_cross_; }
1577 return captured_body_includes_static_track_;
1578 }
1579
1580 // ── Skin overrides ────────────────────────────────────────────────────
1581 // Per-widget appearance, generalising the knob sprite-strip path to the
1582 // fader: instead of baking the captured Figma art (which would freeze the
1583 // thumb at its captured value), the importer derives the track / fill /
1584 // thumb colors from the design and the widget redraws them procedurally
1585 // so the thumb still MOVES with set_value(). Unset → today's theme-token
1586 // behavior (back-compat). Mirrors RangeSlider's accent override.
1587 void set_skin_track_color(canvas::Color c) { track_color_ = c; has_skin_track_ = true; request_repaint(); }
1588 void set_skin_fill_color(canvas::Color c) { fill_color_ = c; has_skin_fill_ = true; request_repaint(); }
1589 void set_skin_thumb_color(canvas::Color c) { thumb_color_ = c; has_skin_thumb_ = true; request_repaint(); }
1595 if (has_skin_thumb_style_) return;
1596 pre_skin_thumb_shape_ = thumb_shape_;
1597 pre_skin_thumb_width_ = thumb_width_;
1598 pre_skin_thumb_height_ = thumb_height_;
1599 pre_skin_thumb_corner_radius_ = thumb_corner_radius_;
1600 has_skin_thumb_style_ = true;
1601 }
1602
1605 thumb_fallback_color_ = c;
1606 has_skin_thumb_fallback_ = true;
1607 // A later panel/theme fallback is a provenance transition, not merely
1608 // another stored palette value. Retire any older per-control override
1609 // so paint can resolve the live control.thumb token again.
1610 has_skin_thumb_ = false;
1612 }
1613
1616 has_skin_thumb_ = false;
1617 has_skin_thumb_fallback_ = false;
1619 }
1620
1625 has_skin_thumb_border_ = false;
1626 if (has_skin_thumb_style_) {
1627 thumb_shape_ = pre_skin_thumb_shape_;
1628 thumb_width_ = pre_skin_thumb_width_;
1629 thumb_height_ = pre_skin_thumb_height_;
1630 thumb_corner_radius_ = pre_skin_thumb_corner_radius_;
1631 has_skin_thumb_style_ = false;
1632 }
1634 }
1635 void set_skin_thumb_border_color(canvas::Color c) { thumb_border_color_ = c; has_skin_thumb_border_ = true; request_repaint(); }
1636 // Outline of the empty track: the lighter edge the captured art draws
1637 // around the dark channel. When set, the skinned fader strokes the track
1638 // rect so it doesn't read as a flat dark slab.
1639 void set_skin_track_border_color(canvas::Color c) { track_border_color_ = c; has_skin_track_border_ = true; request_repaint(); }
1640 // Derived thin track width (logical px). When set, the skinned fader draws
1641 // its track / fill at exactly this width (centerd) instead of a fraction of
1642 // the widget box, matching the captured art's narrow track.
1644 if (w > 0.0f) { skin_track_width_ = w; has_skin_track_width_ = true; request_repaint(); }
1645 }
1646 float skin_track_width() const { return skin_track_width_; }
1647 bool has_skin_track_width() const { return has_skin_track_width_; }
1648 void clear_skin() {
1650 has_skin_track_ = has_skin_fill_ = false;
1651 has_skin_track_border_ = false;
1652 has_skin_track_width_ = false;
1654 }
1655 bool has_skin() const {
1656 return has_skin_track_ || has_skin_fill_ || has_skin_thumb_ ||
1657 has_skin_thumb_border_ || has_skin_track_border_;
1658 }
1659 bool has_skin_track_color() const { return has_skin_track_; }
1660 bool has_skin_fill_color() const { return has_skin_fill_; }
1661 bool has_skin_thumb_color() const { return has_skin_thumb_; }
1663 return has_skin_thumb_fallback_;
1664 }
1665 bool has_skin_thumb_border_color() const { return has_skin_thumb_border_; }
1666 bool has_skin_track_border_color() const { return has_skin_track_border_; }
1667 canvas::Color skin_track_color() const { return track_color_; }
1668 canvas::Color skin_fill_color() const { return fill_color_; }
1669 canvas::Color skin_thumb_color() const { return thumb_color_; }
1671 return thumb_fallback_color_;
1672 }
1673 canvas::Color skin_thumb_border_color() const { return thumb_border_color_; }
1674 canvas::Color skin_track_border_color() const { return track_border_color_; }
1675
1676private:
1677 std::shared_ptr<SpriteStrip> sprite_strip_;
1678 std::shared_ptr<SpriteStrip> captured_body_;
1679 std::shared_ptr<SpriteStrip> captured_indicator_;
1680 float captured_indicator_cross_ = 0.5f;
1681 float captured_body_origin_x_ = 0.0f;
1682 float captured_body_origin_y_ = 0.0f;
1683 float captured_control_natural_w_ = 0.0f;
1684 float captured_control_natural_h_ = 0.0f;
1685 bool captured_body_includes_static_track_ = false;
1686 float captured_raster_origin_x_ = 0.0f;
1687 float captured_raster_origin_y_ = 0.0f;
1688 float captured_raster_scale_ = 0.0f;
1689 canvas::Color track_color_{};
1690 canvas::Color fill_color_{};
1691 canvas::Color thumb_color_{};
1692 canvas::Color thumb_fallback_color_{};
1693 canvas::Color thumb_border_color_{};
1694 canvas::Color track_border_color_{};
1695 bool has_skin_track_ = false;
1696 bool has_skin_fill_ = false;
1697 bool has_skin_thumb_ = false;
1698 bool has_skin_thumb_fallback_ = false;
1699 bool has_skin_thumb_border_ = false;
1700 bool has_skin_track_border_ = false;
1701 float skin_track_width_ = 0.0f;
1702 bool has_skin_track_width_ = false;
1703};
1704
1709std::optional<canvas::Canvas::SDFShape> widget_chart_shape(const View& view);
1710
1713inline float fader_chart_track_thickness(float cross_extent) {
1714 return std::clamp(cross_extent * 0.5f, 8.0f, 24.0f);
1715}
1716
1717// ── UA form-control skins ───────────────────────────────────────────────────
1718// An imported `appearance: auto` form control carries no CSS paint: the
1719// browser's UA theme draws it. RangeSlider and Checkbox can wear Chromium's
1720// cross-platform form-control theme so such a control renders the way the
1721// browser drew it. The types below are shared by those widgets.
1722
1726
1728enum class UaColorScheme { light, dark };
1729
1732
1738 canvas::Color contrast_light, canvas::Color contrast_dark);
1739
1740// ── RangeSlider ──────────────────────────────────────────────────────────────
1741// Generic min/max/step slider mapped to HTML <input type="range">.
1742//
1743// Distinct from Knob (rotary, normalized 0..1) and Fader (DSP linear with
1744// decorative track + large thumb). RangeSlider is the plain rectangular
1745// track + circular handle the web uses for volume / morph / scrubber UIs.
1746// Caller-supplied min/max/step are honoured natively — the bridge does
1747// not preprocess them. Quantisation happens inside the widget so JS-side
1748// callers see the same value the renderer paints.
1749//
1751public:
1753
1756 set_focusable(true);
1757 // The control paints 16pt on its minor axis, which is comfortable to
1758 // look at and uncomfortable to hit. Grow only the hit area to the 44pt
1759 // touch target HitMetrics specifies -- (44 - 16) / 2 = 14pt per edge --
1760 // so the thumb is grabbable without the box, the layout or the paint
1761 // changing. The major axis gets the same 14pt so the thumb is still
1762 // grabbable at either travel extreme, where half of it sits outside
1763 // the track.
1765 const float slop = std::max(0.0f, (target - kMinorAxisExtent) * 0.5f);
1766 set_hit_slop(slop);
1767 }
1768
1772 static constexpr float kMinorAxisExtent = 16.0f;
1773
1774 // ── AccessibilityValueInterface (caller's min/max/step units) ────────
1775 double get_current_value() const override { return value_; }
1776 void set_current_value(double v) override {
1777 const float prev = value_;
1778 set_value(static_cast<float>(v));
1779 if (value_ != prev && on_change) on_change(value_);
1780 }
1781 double get_minimum_value() const override { return min_; }
1782 double get_maximum_value() const override { return max_; }
1783 double get_step_size() const override {
1784 return step_ > 0.0f
1785 ? step_
1787 }
1788
1790 void set_min(float v) { min_ = v; clamp_and_quantize_(); }
1791 float min_value() const { return min_; }
1792
1795 void set_max(float v) { max_ = v; clamp_and_quantize_(); }
1796 float max_value() const { return max_; }
1797
1802 void set_step(float v) { step_ = v; clamp_and_quantize_(); }
1803 float step() const { return step_; }
1804
1818 void set_value(float v, Notify notify = Notify::sync) {
1819 float prev = value_;
1820 value_ = v;
1821 clamp_and_quantize_(notify);
1822 if (value_ != prev) request_repaint();
1823 }
1824 float value() const { return value_; }
1825
1832 void set_modulated_value(float normalized) {
1833 const float v =
1834 std::isfinite(normalized) ? std::clamp(normalized, 0.0f, 1.0f) : value_to_position_();
1835 if (has_modulated_ && v == modulated_)
1836 return;
1837 has_modulated_ = true;
1838 modulated_ = v;
1839 request_repaint_self(kModulatedMarkerHalo);
1840 }
1842 if (!has_modulated_)
1843 return;
1844 has_modulated_ = false;
1845 request_repaint_self(kModulatedMarkerHalo);
1846 }
1847 bool has_modulated_value() const {
1848 return has_modulated_;
1849 }
1850
1852 return modulated_;
1853 }
1854
1855 // Scroll-wheel adjusts the value (hover + wheel), scaled to the range.
1856 bool wants_wheel_value() const override { return true; }
1857 void on_wheel(float delta_y) override {
1858 float prev = value_;
1859 set_value(value_ + (-delta_y) * 0.004f * (max_ - min_));
1860 if (value_ != prev && on_change) on_change(value_);
1861 }
1862
1863 // True while the handle is being dragged so a param binding yields mid-drag.
1864 bool is_gesture_active() const override { return dragging_; }
1865
1866 void set_orientation(Orientation o) { orientation_ = o; }
1867 Orientation orientation() const { return orientation_; }
1868
1873 accent_color_ = c;
1874 has_accent_color_ = true;
1875 }
1876 void clear_accent_color() { has_accent_color_ = false; }
1877 bool has_accent_color() const { return has_accent_color_; }
1878 canvas::Color accent_color() const { return accent_color_; }
1879
1884 using Skin = UaSkin;
1888 if (skin_ == s)
1889 return;
1890 skin_ = s;
1892 }
1893 Skin skin() const {
1894 return skin_;
1895 }
1896
1900 if (color_scheme_ == scheme)
1901 return;
1902 color_scheme_ = scheme;
1904 }
1906 return color_scheme_;
1907 }
1908
1926
1936 static ChromiumRefreshParts
1938 ColorScheme scheme, std::optional<canvas::Color> accent = std::nullopt,
1939 float device_scale = 1.0f);
1940
1943 ChromiumRefreshParts chromium_refresh_parts(float device_scale = 1.0f) const;
1944
1949
1952 void set_track_thickness(float t) { track_thickness_ = std::max(1.0f, t); }
1953 float track_thickness() const { return track_thickness_; }
1954
1961 void set_skew(float s) { skew_ = std::max(0.0001f, s); }
1962 float skew() const { return skew_; }
1965 void set_skew_from_midpoint(float mid_value);
1967 float position_for_value() const { return value_to_position_(); }
1968
1972 std::function<void(float)> on_change;
1973
1976 std::function<void()> on_gesture_begin;
1977 std::function<void()> on_gesture_end;
1978
1979 void paint(canvas::Canvas& canvas) override;
1986 float intrinsic_height() const override { return 16.0f; }
1987 void on_mouse_event(const MouseEvent& event) override;
1988 void on_mouse_drag(Point pos) override;
1989 void on_mouse_enter() override;
1990 void on_mouse_leave() override;
1991 bool wants_mouse_input() const override { return true; }
1992 void advance_animations(float dt) override { hover_scale_.advance(dt); }
1993 float hover_scale() const { return hover_scale_.value(); }
1994
1995private:
1996 ValueAnimation hover_scale_{1.0f};
1999 float position_to_value_(float t) const;
2000
2005 void clamp_and_quantize_(Notify notify = Notify::sync);
2006
2008 void update_from_position_(Point pos);
2009
2012 float value_to_position_() const;
2013
2014 float min_ = 0.0f;
2015 float max_ = 1.0f;
2016 float step_ = 0.0f;
2017 float value_ = 0.0f;
2018 bool has_modulated_ = false;
2019 float modulated_ = 0.0f;
2020 static constexpr float kModulatedMarkerHalo = 2.0f;
2021 float skew_ = 1.0f;
2022 Orientation orientation_ = Orientation::horizontal;
2023 bool dragging_ = false;
2024 float track_thickness_ = 5.0f;
2025 canvas::Color accent_color_{};
2026 bool has_accent_color_ = false;
2027 Skin skin_ = Skin::platform;
2028 ColorScheme color_scheme_ = ColorScheme::light;
2029
2030 void paint_chromium_refresh_(canvas::Canvas& canvas);
2031};
2032
2033// ── Toggle ───────────────────────────────────────────────────────────────────
2034// Boolean on/off switch
2035
2036class Toggle : public View, public CustomShaderHost {
2037public:
2038 Toggle() : thumb_position_(0.0f), hover_opacity_(0.0f) {
2041 // Seed the ARIA state; Toggle::set_on() keeps it in sync. A `toggle`
2042 // (ARIA switch) with no state announces nothing about on/off.
2043 set_access_checked("false");
2044 set_focusable(true);
2045 }
2046
2053 void set_on(bool v, bool animate = true);
2054 bool is_on() const { return on_; }
2055
2063 bool set_on(bool v, Notify notify, bool animate = true);
2064
2065 void set_label(std::string text) {
2066 label_ = std::move(text);
2067 set_derived_access_label(label_); // the visible label is the accessible name
2068 }
2069 const std::string& label() const { return label_; }
2070
2072 std::function<void(bool)> on_toggle;
2073
2074 void paint(canvas::Canvas& canvas) override;
2075 void on_mouse_down(Point pos) override;
2076 void on_mouse_enter() override;
2077 void on_mouse_leave() override;
2078 bool wants_mouse_input() const override { return true; }
2079
2080 // Animation accessors for testing
2081 float thumb_position() const { return thumb_position_.value(); }
2082 float hover_opacity() const { return hover_opacity_.value(); }
2083 void advance_animations(float dt) override;
2084
2085 void set_designed_overlay(bool enabled) { designed_overlay_ = enabled; request_repaint(); }
2086 bool designed_overlay() const { return designed_overlay_; }
2087
2088 // Custom body shader comes from CustomShaderHost.
2089 void set_widget_schema(std::string json) { widget_schema_ = std::move(json); }
2090 const std::string& widget_schema() const { return widget_schema_; }
2091 void set_lottie_json(std::string json) { lottie_json_ = std::move(json); }
2092 const std::string& lottie_json() const { return lottie_json_; }
2093 void set_lottie_time(float t) { lottie_time_ = std::clamp(t, 0.0f, 1.0f); }
2094 float lottie_time() const { return lottie_time_; }
2095
2096
2097private:
2098 bool on_ = false;
2099 std::string label_;
2100 ValueAnimation thumb_position_;
2101 ValueAnimation hover_opacity_;
2102 std::string widget_schema_;
2103 std::string lottie_json_;
2104 float lottie_time_ = 0.0f;
2105 bool designed_overlay_ = false;
2106};
2107
2108// ── Checkbox ────────────────────────────────────────────────────────────────
2109// Rounded-square checkbox with check glyph
2110
2111class Checkbox : public View {
2112public:
2113 // `checkbox`, not `toggle`: ARIA/AT-SPI/UIA all distinguish a checkbox
2114 // from a switch. Toggle/ToggleButton keep `toggle` (ARIA `switch`).
2115 // access_checked is the state slot every bridge reads (NSAccessibility
2116 // accessibilityValue, UIA ToggleState, the AT-SPI CHECKED state): a
2117 // `checkbox` role with an unset state announces the control and then says
2118 // nothing about whether it is checked. Seeded here, kept in sync below.
2124
2125 // Same programmatic repaint contract as Knob::set_value(), including the
2126 // no-change guard for bridge sync/reload loops.
2127 void set_checked(bool v) {
2128 if (checked_ == v) return;
2129 checked_ = v;
2130 set_access_checked(indeterminate_ ? "mixed" : (v ? "true" : "false"));
2132 }
2133
2141 bool set_checked(bool v, Notify notify) {
2142 if (checked_ == v) return false;
2143 checked_ = v;
2144 set_access_checked(indeterminate_ ? "mixed" : (v ? "true" : "false"));
2146 if (notify == Notify::sync) {
2147 if (on_change) on_change(checked_);
2148 } else if (notify == Notify::async && on_change) {
2149 auto fn = on_change;
2150 const bool nv = checked_;
2151 queue_async_notification([fn, nv] { fn(nv); });
2152 }
2153 return true;
2154 }
2155 bool is_checked() const { return checked_; }
2156
2160 enum class Kind { checkbox, radio };
2162 if (kind_ == k)
2163 return;
2164 kind_ = k;
2167 }
2168 Kind kind() const {
2169 return kind_;
2170 }
2171 void set_radio_group(int id) {
2172 radio_group_ = id;
2173 }
2174 int radio_group() const {
2175 return radio_group_;
2176 }
2177
2181 if (indeterminate_ == v)
2182 return;
2183 indeterminate_ = v;
2184 set_access_checked(v ? "mixed" : (checked_ ? "true" : "false"));
2186 }
2187 bool is_indeterminate() const {
2188 return indeterminate_;
2189 }
2190
2193 using Skin = UaSkin;
2197 if (skin_ == s)
2198 return;
2199 skin_ = s;
2201 }
2202 Skin skin() const {
2203 return skin_;
2204 }
2206 if (color_scheme_ == scheme)
2207 return;
2208 color_scheme_ = scheme;
2210 }
2212 return color_scheme_;
2213 }
2215 accent_color_ = c;
2217 }
2219 accent_color_.reset();
2221 }
2222 std::optional<canvas::Color> accent_color() const {
2223 return accent_color_;
2224 }
2225
2233 float radius = 0;
2234 ColorScheme scheme = ColorScheme::light;
2235 std::optional<canvas::Color> lighten;
2238 std::optional<canvas::Color> border;
2240 std::optional<canvas::Color> accent_fill;
2242 bool has_check = false;
2243 std::array<Point, 3> check{};
2244 float check_width = 0;
2245 std::optional<Rect> dash;
2246 std::optional<Rect> dot;
2247 };
2248
2254 chromium_refresh_parts(Rect bounds, Kind kind, bool checked, bool indeterminate,
2256 std::optional<canvas::Color> accent = std::nullopt);
2258
2261
2262 std::function<void(bool)> on_change;
2263
2264 void paint(canvas::Canvas& canvas) override;
2265 void on_mouse_down(Point pos) override;
2266 bool wants_mouse_input() const override { return true; }
2267
2268private:
2269 bool checked_ = false;
2270 bool indeterminate_ = false;
2271 Kind kind_ = Kind::checkbox;
2272 int radio_group_ = 0;
2273 Skin skin_ = Skin::platform;
2274 ColorScheme color_scheme_ = ColorScheme::light;
2275 std::optional<canvas::Color> accent_color_;
2276
2277 void paint_chromium_refresh_(canvas::Canvas& canvas);
2278};
2279
2280// ── ButtonSurface ───────────────────────────────────────────────────────────
2281// The themed surface of an imported `appearance: auto` HTML <button>: Chromium's
2282// push-button background, border and per-state colours, plus its focus-visible
2283// ring. It paints nothing else; the button's content (labels, icons) is its
2284// children, laid out like any container's. Only an explicitly constructed
2285// ButtonSurface wears this skin; TextButton and generated UI are unaffected.
2286//
2287// The focus ring reaches one CSS px (two device px at scale 2) outside the
2288// view's bounds, as Chromium's does; it is painted in paint() before the
2289// children, which never reach the 2px band at the edge it covers.
2290
2291class ButtonSurface : public View {
2292 public:
2295
2299
2303 if (color_scheme_ == scheme)
2304 return;
2305 color_scheme_ = scheme;
2307 }
2309 return color_scheme_;
2310 }
2311
2314 if (pressed_ == pressed)
2315 return;
2316 pressed_ = pressed;
2318 }
2319 bool is_pressed() const {
2320 return pressed_;
2321 }
2322
2325 if (focus_visible_ == visible)
2326 return;
2327 focus_visible_ = visible;
2329 }
2330 bool is_focus_visible() const {
2331 return focus_visible_;
2332 }
2333
2337
2359
2366 ColorScheme scheme, bool focus_visible,
2367 float device_scale = 1.0f);
2370 ChromiumRefreshParts chromium_refresh_parts(float device_scale = 1.0f) const;
2371
2372 void paint(canvas::Canvas& canvas) override;
2373
2374 private:
2375 ColorScheme color_scheme_ = ColorScheme::light;
2376 bool pressed_ = false;
2377 bool focus_visible_ = false;
2378};
2379
2380// ── ToggleButton ────────────────────────────────────────────────────────────
2381// Full-width rounded button that toggles on/off
2382
2383class ToggleButton : public View {
2384public:
2390
2391 // Same programmatic repaint contract as Knob::set_value(), including the
2392 // no-change guard for bridge sync/reload loops.
2393 void set_on(bool v) {
2394 if (on_ == v) return;
2395 on_ = v;
2396 set_access_checked(v ? "true" : "false");
2398 }
2399
2407 bool set_on(bool v, Notify notify) {
2408 if (on_ == v) return false;
2409 on_ = v;
2410 set_access_checked(v ? "true" : "false");
2412 if (notify == Notify::sync) {
2413 if (on_toggle) on_toggle(on_);
2414 } else if (notify == Notify::async && on_toggle) {
2415 auto fn = on_toggle;
2416 const bool nv = on_;
2417 queue_async_notification([fn, nv] { fn(nv); });
2418 }
2419 return true;
2420 }
2421 bool is_on() const { return on_; }
2422
2423 void set_label(std::string text) {
2424 if (label_ == text) return;
2425 label_ = std::move(text);
2426 set_derived_access_label(label_); // the visible label is the accessible name
2428 }
2429 const std::string& label() const { return label_; }
2430
2437 void set_corner_radius(float radius) { corner_radius_ = std::max(0.0f, radius); request_repaint(); }
2438 void set_font_size(float size) { font_size_ = std::max(1.0f, size); request_repaint(); }
2439
2446 void set_radio_group(int id) { radio_group_ = id; }
2447 int radio_group() const { return radio_group_; }
2448
2449 const std::optional<canvas::Color>& on_background_color_override() const { return on_background_color_; }
2450 const std::optional<canvas::Color>& off_background_color_override() const { return off_background_color_; }
2451 const std::optional<canvas::Color>& on_text_color_override() const { return on_text_color_; }
2452 const std::optional<canvas::Color>& off_text_color_override() const { return off_text_color_; }
2453 const std::optional<canvas::Color>& on_border_color_override() const { return on_border_color_; }
2454 const std::optional<canvas::Color>& off_border_color_override() const { return off_border_color_; }
2455 std::optional<float> corner_radius_override() const { return corner_radius_; }
2456 std::optional<float> font_size_override() const { return font_size_; }
2459 void set_designed_overlay(bool enabled) { designed_overlay_ = enabled; request_repaint(); }
2460 bool designed_overlay() const { return designed_overlay_; }
2461
2462 std::function<void(bool)> on_toggle;
2463
2464 void paint(canvas::Canvas& canvas) override;
2465 void on_mouse_down(Point pos) override;
2466 bool wants_mouse_input() const override { return true; }
2467
2468 float intrinsic_height() const override { return 36.0f; }
2469
2470private:
2471 bool on_ = false;
2472 int radio_group_ = 0;
2473 std::string label_;
2474 std::optional<canvas::Color> on_background_color_;
2475 std::optional<canvas::Color> off_background_color_;
2476 std::optional<canvas::Color> on_text_color_;
2477 std::optional<canvas::Color> off_text_color_;
2478 std::optional<canvas::Color> on_border_color_;
2479 std::optional<canvas::Color> off_border_color_;
2480 std::optional<float> corner_radius_;
2481 std::optional<float> font_size_;
2482 bool designed_overlay_ = false;
2483};
2484
2485// ── Icon ────────────────────────────────────────────────────────────────────
2486// Simple vector icons drawn with canvas strokes and fills
2487
2488class Icon : public View {
2489public:
2490 enum class Type { image_upload, send, search, close };
2491
2492 Icon() {}
2493 explicit Icon(Type t) : type_(t) {}
2494
2495 void set_type(Type t) { type_ = t; }
2496 Type type() const { return type_; }
2497
2498 void paint(canvas::Canvas& canvas) override;
2499
2500private:
2501 Type type_ = Type::image_upload;
2502};
2503
2504// ── ImageView ───────────────────────────────────────────────────────────────
2505// Displays an image loaded from a file path
2506
2507class ImageCache; // fwd; include pulp/view/image_cache.hpp in consumers
2508
2509class ImageView : public View {
2510public:
2512
2515 void set_image_path(const std::string& path) {
2516 set_image_source(path.empty() ? std::string{} : "file://" + path);
2517 }
2518 const std::string& image_path() const { return path_; }
2519
2524 void set_image_source(const std::string& uri) {
2525 if (uri != path_) {
2526 path_ = uri;
2527 loaded_ = false;
2528 cached_data_.clear();
2529 cached_data_key_.clear();
2530 cached_data_width_ = 0.0f;
2531 cached_data_height_ = 0.0f;
2532 inline_decode_failed_ = false;
2533 }
2534 }
2535 const std::string& image_source() const { return path_; }
2536
2541 void set_image_cache(ImageCache* cache) { cache_ = cache; }
2542 ImageCache* image_cache() const { return cache_; }
2543
2550 void set_fill_value(float value) {
2551 fill_value_ = value;
2553 }
2554 float fill_value() const { return fill_value_; }
2556 fill_color_ = color;
2558 }
2559 bool has_fill() const { return fill_value_ >= 0.0f; }
2560
2570 void set_fill_gradient(std::vector<canvas::Color> stops) {
2571 fill_gradient_ = std::move(stops);
2573 }
2574 const std::vector<canvas::Color>& fill_gradient() const { return fill_gradient_; }
2575 bool has_fill_gradient() const { return fill_gradient_.size() >= 2; }
2576
2577 void paint(canvas::Canvas& canvas) override;
2578
2579private:
2581 canvas::Color fill_gradient_color_at(float t) const;
2582
2583 std::string path_;
2584 bool loaded_ = false;
2585 std::vector<uint8_t> cached_data_; // File bytes cached after first successful load
2586 std::string cached_data_key_; // SHA-256 hex of cached_data_; empty when unset
2587 float cached_data_width_ = 0.0f; // Intrinsic size of cached_data_; 0 until measured
2588 float cached_data_height_ = 0.0f;
2589 bool inline_decode_failed_ = false; // Inline bytes no decoder accepted; never retried
2590 ImageCache* cache_ = nullptr; // optional; owned externally
2591 float fill_value_ = -1.0f; // <0 disables the silhouette fill overlay
2592 canvas::Color fill_color_ = canvas::Color::rgba(0.49f, 0.42f, 1.0f, 0.55f);
2593 std::vector<canvas::Color> fill_gradient_; // ≥2 stops ⇒ gradient fill
2594};
2595
2596// ── XYPad ────────────────────────────────────────────────────────────────────
2597// 2D parameter control surface (e.g., filter frequency × resonance)
2598
2599class XYPad : public View {
2600public:
2601 XYPad() = default;
2602
2603 void set_x(float v) { x_ = std::clamp(v, 0.0f, 1.0f); request_repaint(); }
2604 void set_y(float v) { y_ = std::clamp(v, 0.0f, 1.0f); request_repaint(); }
2605 float x_value() const { return x_; }
2606 float y_value() const { return y_; }
2607
2614 void set_modulated_x(float normalized) {
2615 set_modulated_axis(has_mod_x_, mod_x_, x_, normalized);
2616 }
2617 void set_modulated_y(float normalized) {
2618 set_modulated_axis(has_mod_y_, mod_y_, y_, normalized);
2619 }
2621 clear_modulated_axis(has_mod_x_);
2622 }
2624 clear_modulated_axis(has_mod_y_);
2625 }
2626 bool has_modulated_value() const {
2627 return has_mod_x_ || has_mod_y_;
2628 }
2629
2630 float modulated_display_x() const {
2631 return has_mod_x_ ? mod_x_ : x_;
2632 }
2633 float modulated_display_y() const {
2634 return has_mod_y_ ? mod_y_ : y_;
2635 }
2636
2637 void set_x_label(std::string l) { x_label_ = std::move(l); }
2638 void set_y_label(std::string l) { y_label_ = std::move(l); }
2639
2640 std::function<void(float, float)> on_change;
2641 std::function<void()> on_gesture_begin;
2642 std::function<void()> on_gesture_end;
2643
2644 void paint(canvas::Canvas& canvas) override;
2645 void on_mouse_down(Point pos) override;
2646 void on_mouse_drag(Point pos) override;
2647 void on_mouse_up(Point pos) override;
2648 bool wants_mouse_input() const override { return true; }
2649
2650 // True while the puck is being dragged so a param binding yields mid-drag.
2651 bool is_gesture_active() const override { return dragging_; }
2652
2653private:
2654 void update_from_pos(Point pos);
2655 void set_modulated_axis(bool& has, float& slot, float base, float normalized) {
2656 const float v = std::isfinite(normalized) ? std::clamp(normalized, 0.0f, 1.0f) : base;
2657 if (has && v == slot)
2658 return;
2659 has = true;
2660 slot = v;
2661 request_repaint_self(kModulatedMarkerHalo);
2662 }
2663 void clear_modulated_axis(bool& has) {
2664 if (!has)
2665 return;
2666 has = false;
2667 request_repaint_self(kModulatedMarkerHalo);
2668 }
2669 static constexpr float kModulatedMarkerHalo = 2.0f;
2670 float x_ = 0.5f, y_ = 0.5f;
2671 bool has_mod_x_ = false, has_mod_y_ = false;
2672 float mod_x_ = 0.0f, mod_y_ = 0.0f;
2673 std::string x_label_, y_label_;
2674 bool dragging_ = false;
2675};
2676
2677// ── Panel ────────────────────────────────────────────────────────────────────
2678// Styled container with background, border, and rounding. For visual chrome.
2679
2680class Panel : public View {
2681public:
2682 // Seed the View-side radius slot so the default 8px rounding paints.
2684
2685 void set_background_token(std::string token) { bg_token_ = std::move(token); }
2686 void set_border_token(std::string token) { border_token_ = std::move(token); }
2687 // Route through View::set_border_radius so the px slot the painter reads is
2688 // updated and the pct slot is cleared, matching documented semantics.
2690 void set_border_width(float w) { border_width_ = w; }
2691
2692 const std::string& background_token() const { return bg_token_; }
2693 const std::string& border_token() const { return border_token_; }
2694 float corner_radius() const { return View::corner_radius(); }
2695 float border_width() const { return border_width_; }
2696
2697 void paint(canvas::Canvas& canvas) override;
2698
2699private:
2700 std::string bg_token_ = "bg.surface";
2701 std::string border_token_ = "control.border";
2702 float border_width_ = 1.0f;
2703};
2704
2705// ── GroupBox ───────────────────────────────────────────────────────────────
2706// A titled container: rounded frame, a title chip at the top-left, and an
2707// optional collapse chevron at the top-right. Collapsing hides the child
2708// content (the caller sizes a collapsed box to header height). Matches the
2709// Figma "Group Box" specimen (default / collapsible-expanded / collapsed /
2710// empty-title-only). Children are added via add_child() and positioned by the
2711// caller within the content area (origin = content_top()).
2712class GroupBox : public View {
2713public:
2714 static constexpr float header_height = 30.0f;
2715
2716 void set_title(std::string t) { title_ = std::move(t); request_repaint(); }
2717 const std::string& title() const { return title_; }
2718
2719 void set_collapsible(bool c) { collapsible_ = c; request_repaint(); }
2720 bool collapsible() const { return collapsible_; }
2721
2722 // Collapsed hides all child content; expanded shows it. `notify` controls
2723 // whether the change reaches `on_toggle`: `Notify::sync` (default) fires it,
2724 // `Notify::none` writes silently (syncing FROM the bound state), and
2725 // `Notify::async` defers it to the next flush. Fires only on a real change.
2726 void set_collapsed(bool c, Notify notify = Notify::sync);
2727 bool collapsed() const { return collapsed_; }
2728
2729 // Y at which child content begins (below the header band).
2730 float content_top() const { return header_height + 6.0f; }
2731
2732 std::function<void(bool collapsed)> on_toggle;
2733
2734 void paint(canvas::Canvas& canvas) override;
2735 void on_mouse_event(const MouseEvent& event) override;
2736 bool wants_mouse_input() const override { return true; }
2737
2738private:
2739 void apply_child_visibility();
2740 std::string title_;
2741 bool collapsible_ = false;
2742 bool collapsed_ = false;
2743};
2744
2745// ── DualRangeSlider ──────────────────────────────────────────────────────────
2746// Two-thumb min↔max range selector (the Figma "Range Slider"): a track with a
2747// filled segment between an independently-draggable low and high thumb. (The
2748// single-thumb pulp::view::RangeSlider is a plain value slider; this is the
2749// dual-handle variant.) Horizontal or vertical; optional disabled state.
2750class DualRangeSlider : public View {
2751public:
2753 void set_orientation(Orientation o) { orientation_ = o; request_repaint(); }
2754 void set_range(float min, float max) { min_ = min; max_ = std::max(min, max); clamp_(); }
2755 void set_low(float v) { low_ = v; clamp_(); }
2756 void set_high(float v) { high_ = v; clamp_(); }
2757 float low() const { return low_; }
2758 float high() const { return high_; }
2759 float min_value() const { return min_; }
2760 float max_value() const { return max_; }
2761 void set_enabled(bool e) { enabled_ = e; request_repaint(); }
2762 bool enabled() const { return enabled_; }
2763 // When true, a dragged thumb stops at the other thumb instead of crossing it
2764 // (low ≤ high always). Default false (ends move freely / may cross).
2765 void set_no_cross(bool b) { no_cross_ = b; }
2766 bool no_cross() const { return no_cross_; }
2767
2768 // Fired on a thumb drag with the (possibly-updated) low and high values.
2769 std::function<void(float low, float high)> on_change;
2770
2771 void paint(canvas::Canvas& canvas) override;
2772 void on_mouse_down(Point pos) override;
2773 void on_mouse_drag(Point pos) override;
2774 void on_mouse_up(Point pos) override;
2775 bool wants_mouse_input() const override { return true; }
2776
2777private:
2778 void clamp_();
2779 float pos_for_(float v) const; // value → 0..1 along the track
2780 float value_for_pos_(float t) const; // 0..1 → value
2781 float pointer_t_(Point pos) const; // pointer → 0..1 along the track
2782 void apply_(Point pos); // move the active thumb to the pointer
2783 Orientation orientation_ = Orientation::horizontal;
2784 float min_ = 0.0f, max_ = 1.0f, low_ = 0.25f, high_ = 0.70f;
2785 bool enabled_ = true;
2786 bool no_cross_ = false;
2787 int drag_ = -1; // 0 = low thumb, 1 = high thumb, -1 = none
2788};
2789
2790// ── InlineValueEditor ────────────────────────────────────────────────────────
2791// A click-to-type numeric value readout (the Figma "Inline Value Editor"): shows
2792// `value + suffix` idle; click to edit (caret), Enter commits, Esc cancels. A
2793// committed value outside [min,max] shows a danger ring and is rejected. Pair it
2794// with a Knob/Fader (the readout drives the control via on_change; the control
2795// updates the readout via set_value). States: idle / editing / invalid / disabled.
2796class InlineValueEditor : public View {
2797public:
2799
2800 void set_value(double v) { value_ = v; request_repaint(); }
2801 double value() const { return value_; }
2802 void set_range(double min, double max) { min_ = min; max_ = max; }
2803 void set_suffix(std::string s) { suffix_ = std::move(s); request_repaint(); }
2804 void set_decimals(int d) { decimals_ = d; request_repaint(); }
2805 void set_enabled(bool e) { enabled_ = e; if (!e) editing_ = false; request_repaint(); }
2806 bool enabled() const { return enabled_; }
2807 bool editing() const { return editing_; }
2808 bool invalid() const { return invalid_; }
2809
2810 // Fired when a typed value is committed in range.
2811 std::function<void(double)> on_change;
2812
2816
2818 void set_caret_style(CaretStyle style) { caret_style_ = style; request_repaint(); }
2819 CaretStyle caret_style() const { return caret_style_; }
2820
2822 void set_caret_blink(const CaretBlinkConfig& config) {
2823 caret_blink_.set_config(config);
2825 }
2826 const CaretBlinkConfig& caret_blink() const { return caret_blink_.config(); }
2827
2829
2830 void paint(canvas::Canvas& canvas) override;
2831 void on_mouse_down(Point pos) override;
2832 void on_text_input(const TextInputEvent& event) override;
2833 bool on_key_event(const KeyEvent& event) override;
2834 void on_focus_changed(bool gained) override;
2837 void on_frame_clock_changed() override;
2838 bool wants_mouse_input() const override { return true; }
2839
2840private:
2841 std::string display_() const; // value + suffix
2842 void subscribe_caret_blink_();
2843 void unsubscribe_caret_blink_();
2844 double value_ = 0.0, min_ = -1e9, max_ = 1e9;
2845 int decimals_ = 1;
2846 std::string suffix_;
2847 bool enabled_ = true, editing_ = false, invalid_ = false;
2848 std::string edit_buffer_;
2849 CaretBlink caret_blink_;
2850 CaretStyle caret_style_ = default_caret_style();
2851 int caret_blink_sub_ = -1;
2852 FrameClock* caret_blink_clock_ = nullptr;
2855};
2856
2857// ── WaveformRecorder ─────────────────────────────────────────────────────────
2858// Three-state recorder control (Ink & Signal "Recorder" component). One widget
2859// composes a filled waveform display, a bottom level meter with a draggable
2860// threshold thumb, a center transport button, and a top-right status badge,
2861// and re-skins all four for each state:
2862//
2863// armed — faint/empty waveform, red record dot, "READY" badge; the
2864// threshold thumb is draggable to arm the capture trigger.
2865// recording — waveform tinted with accent.error, stop square, live "REC"
2866// badge; the meter shows the incoming level but the thumb is
2867// locked (you can't re-arm mid-take).
2868// captured — waveform in accent.primary (teal), play triangle, "CAPTURED"
2869// badge with a check; the meter is inert.
2870//
2871// Clicking the center transport button advances the state in the cycle
2872// armed → recording → captured → armed and fires on_state_change plus the
2873// matching transport callback (on_record / on_stop / on_play). All chrome is
2874// drawn from theme tokens so it stays legible on the dark Ink & Signal theme.
2875class WaveformRecorder : public View {
2876public:
2877 enum class State { armed, recording, captured };
2878
2883
2889 State state() const { return state_; }
2890 std::function<void(State)> on_state_change;
2891
2893 void set_waveform(std::vector<float> samples) {
2894 waveform_ = std::move(samples);
2896 }
2897 const std::vector<float>& waveform() const { return waveform_; }
2898
2900 void set_level(float level) {
2901 float c = std::clamp(level, 0.0f, 1.0f);
2902 if (c == level_) return;
2903 level_ = c;
2905 }
2906 float level() const { return level_; }
2907
2911 float c = std::clamp(threshold, 0.0f, 1.0f);
2912 if (c == threshold_) return;
2913 threshold_ = c;
2915 }
2916 float threshold() const { return threshold_; }
2917 std::function<void(float)> on_threshold_change;
2918
2920 std::function<void()> on_record;
2921 std::function<void()> on_stop;
2922 std::function<void()> on_play;
2923
2924 void paint(canvas::Canvas& canvas) override;
2925 void on_mouse_down(Point pos) override;
2926 void on_mouse_drag(Point pos) override;
2927 void on_mouse_up(Point pos) override;
2928 bool wants_mouse_input() const override { return true; }
2929
2930private:
2932 Rect meter_rect_() const;
2934 Rect waveform_rect_() const;
2935 float transport_radius_() const;
2936 Point transport_center_() const;
2939 void advance_state_();
2941 void update_threshold_from_x_(float x);
2942
2943 State state_ = State::armed;
2944 std::vector<float> waveform_;
2945 float level_ = 0.0f;
2946 float threshold_ = 0.5f;
2947 bool dragging_threshold_ = false;
2948};
2949
2950} // namespace pulp::view
2951
2952// clang-format off
2953#if defined(PULP_VIEW_HAS_AUDIO_WIDGETS)
2955#endif // PULP_VIEW_HAS_AUDIO_WIDGETS
2956// clang-format on
Text-caret appearance and blink policy, shared by every editable widget.
Attributed string — a sequence of styled text spans.
Definition attributed_string.hpp:40
Definition canvas.hpp:240
Interface for accessible values (sliders, knobs, progress bars).
Definition accessibility.hpp:16
virtual std::string get_value_string() const
Value as a human-readable string (e.g., "50%" or "-6 dB").
virtual double get_step_size() const
Step increment for keyboard navigation.
Definition accessibility.hpp:36
ChromiumRefreshParts chromium_refresh_parts(float device_scale=1.0f) const
The parts for this view, in self_paint_bounds(): the captured, CSS-edge-snapped box when the importer...
ColorScheme color_scheme() const
Definition widgets.hpp:2308
void set_color_scheme(ColorScheme scheme)
The page colour scheme the palette resolves from (CSS color-scheme).
Definition widgets.hpp:2302
bool is_focus_visible() const
Definition widgets.hpp:2330
UaColorScheme ColorScheme
Definition widgets.hpp:2293
bool is_pressed() const
Definition widgets.hpp:2319
ControlState control_state() const
Disabled when not enabled, then pressed, then hovered under the pointer, otherwise normal.
void paint(canvas::Canvas &canvas) override
void set_pressed(bool pressed)
CSS :active: the pressed palette while true.
Definition widgets.hpp:2313
void set_focus_visible(bool visible)
CSS :focus-visible: paints Chromium's two-colour focus ring.
Definition widgets.hpp:2324
ButtonSurface()
Definition widgets.hpp:2296
static ChromiumRefreshParts chromium_refresh_parts(Rect bounds, ControlState state, ColorScheme scheme, bool focus_visible, float device_scale=1.0f)
Pure layout + palette of Chromium's push button for bounds.
UaControlState ControlState
Definition widgets.hpp:2294
Kind
checkbox toggles on click.
Definition widgets.hpp:2160
@ checkbox
Definition widgets.hpp:2160
@ radio
Definition widgets.hpp:2160
UaControlState ControlState
Definition widgets.hpp:2195
Skin skin() const
Definition widgets.hpp:2202
void set_radio_group(int id)
Definition widgets.hpp:2171
int radio_group() const
Definition widgets.hpp:2174
Kind kind() const
Definition widgets.hpp:2168
void set_indeterminate(bool v)
HTML indeterminate (checkbox only): painted as a dash, reported as "mixed".
Definition widgets.hpp:2180
UaColorScheme ColorScheme
Definition widgets.hpp:2194
void set_skin(Skin s)
Definition widgets.hpp:2196
std::function< void(bool)> on_change
Definition widgets.hpp:2262
bool is_indeterminate() const
Definition widgets.hpp:2187
void clear_accent_color()
Definition widgets.hpp:2218
static ChromiumRefreshParts chromium_refresh_parts(Rect bounds, Kind kind, bool checked, bool indeterminate, ControlState state, ColorScheme scheme, std::optional< canvas::Color > accent=std::nullopt)
Pure layout + palette of Chromium's checkbox / radio for bounds.
ChromiumRefreshParts chromium_refresh_parts() const
void set_kind(Kind k)
Definition widgets.hpp:2161
std::optional< canvas::Color > accent_color() const
Definition widgets.hpp:2222
Checkbox()
Definition widgets.hpp:2119
void set_accent_color(canvas::Color c)
Definition widgets.hpp:2214
ControlState control_state() const
Disabled when not enabled, hovered under the pointer, otherwise normal.
UaSkin Skin
Skin, colour scheme and CSS accent-color; see UaSkin.
Definition widgets.hpp:2193
bool is_checked() const
Definition widgets.hpp:2155
void paint(canvas::Canvas &canvas) override
void set_color_scheme(ColorScheme scheme)
Definition widgets.hpp:2205
void set_checked(bool v)
Definition widgets.hpp:2127
ColorScheme color_scheme() const
Definition widgets.hpp:2211
void on_mouse_down(Point pos) override
bool set_checked(bool v, Notify notify)
Notifying overload.
Definition widgets.hpp:2141
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2266
Mixin for widgets whose body can be painted by a custom SkSL shader.
Definition custom_shader_host.hpp:27
Definition widgets.hpp:2750
void set_orientation(Orientation o)
Definition widgets.hpp:2753
bool enabled() const
Definition widgets.hpp:2762
void paint(canvas::Canvas &canvas) override
void on_mouse_up(Point pos) override
void on_mouse_down(Point pos) override
float max_value() const
Definition widgets.hpp:2760
void on_mouse_drag(Point pos) override
bool no_cross() const
Definition widgets.hpp:2766
void set_high(float v)
Definition widgets.hpp:2756
std::function< void(float low, float high)> on_change
Definition widgets.hpp:2769
float min_value() const
Definition widgets.hpp:2759
float high() const
Definition widgets.hpp:2758
void set_no_cross(bool b)
Definition widgets.hpp:2765
void set_low(float v)
Definition widgets.hpp:2755
void set_enabled(bool e)
Definition widgets.hpp:2761
void set_range(float min, float max)
Definition widgets.hpp:2754
float low() const
Definition widgets.hpp:2757
Orientation
Definition widgets.hpp:2752
@ horizontal
Definition widgets.hpp:2752
@ vertical
Definition widgets.hpp:2752
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2775
Orientation
Definition widgets.hpp:1339
@ horizontal
Definition widgets.hpp:1339
@ vertical
Definition widgets.hpp:1339
void clear_skin_thumb_style()
Retire every scripted thumb-specific override while leaving authored track/fill styling intact.
Definition widgets.hpp:1623
void clear_skin_thumb_colors()
Return thumb colour authority entirely to the live theme.
Definition widgets.hpp:1615
bool has_skin_track_border_color() const
Definition widgets.hpp:1666
void set_widget_schema(std::string json)
Definition widgets.hpp:1499
bool has_skin_fill_color() const
Definition widgets.hpp:1660
const std::string & lottie_json() const
Definition widgets.hpp:1502
bool wants_wheel_value() const override
True if this widget adjusts its VALUE on a scroll-wheel over it (knobs, faders, sliders,...
Definition widgets.hpp:1422
float thumb_height() const
Definition widgets.hpp:1447
void set_modulated_value(float normalized)
Display-only played value while a modulator moves this parameter around value().
Definition widgets.hpp:1400
void set_skew(float s)
Skew / response curve (see RangeSlider::set_skew).
Definition widgets.hpp:1484
void set_render_style(WidgetRenderStyle s)
Definition widgets.hpp:1358
void on_mouse_up(Point pos) override
bool has_skin_thumb_color() const
Definition widgets.hpp:1661
Orientation orientation() const
Definition widgets.hpp:1432
void set_skew_from_midpoint(float mid_normalized)
Definition widgets.hpp:1486
bool is_gesture_active() const override
True while the user is actively manipulating this widget's value with a pointer drag (knob turn,...
Definition widgets.hpp:1429
void set_sprite_strip(std::shared_ptr< SpriteStrip > strip)
Sprite strip: designer-created filmstrip for fader appearance.
Definition widgets.hpp:1538
bool has_skin_track_color() const
Definition widgets.hpp:1659
std::function< void(float)> on_change
Called when the value changes.
Definition widgets.hpp:1464
void set_skin_track_width(float w)
Definition widgets.hpp:1643
void on_mouse_drag(Point pos) override
void set_lottie_time(float t)
Definition widgets.hpp:1503
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:1475
float value_for_position(float p) const
Definition widgets.hpp:1493
bool has_skin_thumb_border_color() const
Definition widgets.hpp:1665
void on_mouse_enter() override
void set_label(std::string text)
Definition widgets.hpp:1455
void set_thumb_shape(ThumbShape shape)
Definition widgets.hpp:1434
bool set_value(float v, Notify notify)
Notifying overload.
Definition widgets.hpp:1378
bool has_captured_indicator_art() const
Definition widgets.hpp:1572
void set_orientation(Orientation o)
Definition widgets.hpp:1431
void set_skin_fill_color(canvas::Color c)
Definition widgets.hpp:1588
void clear_modulated_value()
Definition widgets.hpp:1408
void set_current_value(double v) override
Set the value (if editable).
Definition widgets.hpp:1350
void set_skin_track_border_color(canvas::Color c)
Definition widgets.hpp:1639
float position_for_value() const
Track position [0,1] (skew-mapped) for the current value, and the inverse used while dragging.
Definition widgets.hpp:1492
WidgetRenderStyle render_style() const
Definition widgets.hpp:1359
canvas::Color skin_thumb_fallback_color() const
Definition widgets.hpp:1670
Fader()
Definition widgets.hpp:1342
float lottie_time() const
Definition widgets.hpp:1504
float hover_scale() const
Definition widgets.hpp:1478
float value() const
Definition widgets.hpp:1392
bool captured_body_includes_static_track() const
Definition widgets.hpp:1576
bool has_skin_thumb_fallback_color() const
Definition widgets.hpp:1662
float captured_raster_origin_x() const
Definition widgets.hpp:1570
void set_skin_track_color(canvas::Color c)
Definition widgets.hpp:1587
float captured_raster_origin_y() const
Definition widgets.hpp:1571
canvas::Color skin_track_color() const
Definition widgets.hpp:1667
void set_thumb_size(float width, float height)
Definition widgets.hpp:1441
canvas::Color skin_thumb_border_color() const
Definition widgets.hpp:1673
float thumb_corner_radius() const
Definition widgets.hpp:1453
float captured_indicator_cross() const
Definition widgets.hpp:1575
void on_mouse_down(Point pos) override
void on_wheel(float delta_y) override
Adjust the widget's value by a scroll-wheel delta (delta_y positive = scrolled down).
Definition widgets.hpp:1423
canvas::Color skin_thumb_color() const
Definition widgets.hpp:1669
void set_skin_thumb_color(canvas::Color c)
Definition widgets.hpp:1589
ThumbShape thumb_shape() const
Definition widgets.hpp:1439
canvas::Color skin_fill_color() const
Definition widgets.hpp:1668
const std::string & widget_schema() const
Definition widgets.hpp:1500
bool has_modulated_value() const
Definition widgets.hpp:1414
void begin_skin_thumb_style()
Snapshot the non-skin thumb geometry before a scripted skin takes ownership.
Definition widgets.hpp:1594
void set_value(float v)
Definition widgets.hpp:1364
bool has_skin() const
Definition widgets.hpp:1655
double get_minimum_value() const override
Minimum value.
Definition widgets.hpp:1355
std::function< void()> on_gesture_begin
Definition widgets.hpp:1465
float captured_raster_scale() const
Definition widgets.hpp:1569
ThumbShape
Definition widgets.hpp:1340
@ circle
Definition widgets.hpp:1340
@ rectangle
Definition widgets.hpp:1340
canvas::Color skin_track_border_color() const
Definition widgets.hpp:1674
double get_current_value() const override
Current value as a number.
Definition widgets.hpp:1349
bool has_skin_track_width() const
Definition widgets.hpp:1647
void on_mouse_event(const MouseEvent &event) override
Mouse down with full event context.
void advance_animations(float dt) override
Advance any per-widget non-CSS animation state (hover glows, thumb tweens, spinner phase) by dt secon...
std::function< void()> on_gesture_end
Definition widgets.hpp:1466
const std::shared_ptr< SpriteStrip > & sprite_strip() const
Definition widgets.hpp:1539
void clear_skin()
Definition widgets.hpp:1648
float modulated_display_value() const
Definition widgets.hpp:1417
void on_mouse_leave() override
float thumb_width() const
Definition widgets.hpp:1446
void set_skin_thumb_fallback_color(canvas::Color c)
Panel-wide fallback for a thumb.
Definition widgets.hpp:1604
void set_skin_thumb_border_color(canvas::Color c)
Definition widgets.hpp:1635
double get_maximum_value() const override
Maximum value.
Definition widgets.hpp:1356
float skin_track_width() const
Definition widgets.hpp:1646
void set_captured_raster_origin(float x, float y, float scale)
Definition widgets.hpp:1564
void set_thumb_corner_radius(float radius)
Definition widgets.hpp:1449
void set_lottie_json(std::string json)
Definition widgets.hpp:1501
float skew() const
Definition widgets.hpp:1485
void set_captured_art(std::shared_ptr< SpriteStrip > body, std::shared_ptr< SpriteStrip > indicator, float indicator_cross=0.5f, float body_origin_x=0.0f, float body_origin_y=0.0f, float control_natural_w=0.0f, float control_natural_h=0.0f, bool body_includes_static_track=false)
Browser-captured fader art.
Definition widgets.hpp:1546
void paint(canvas::Canvas &canvas) override
const std::string & label() const
Definition widgets.hpp:1461
Definition frame_clock.hpp:14
Definition widgets.hpp:2712
void on_mouse_event(const MouseEvent &event) override
Mouse down with full event context.
static constexpr float header_height
Definition widgets.hpp:2714
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2736
void set_collapsed(bool c, Notify notify=Notify::sync)
const std::string & title() const
Definition widgets.hpp:2717
void set_title(std::string t)
Definition widgets.hpp:2716
float content_top() const
Definition widgets.hpp:2730
void set_collapsible(bool c)
Definition widgets.hpp:2719
void paint(canvas::Canvas &canvas) override
bool collapsed() const
Definition widgets.hpp:2727
std::function< void(bool collapsed)> on_toggle
Definition widgets.hpp:2732
bool collapsible() const
Definition widgets.hpp:2720
void paint(canvas::Canvas &canvas) override
Icon()
Definition widgets.hpp:2492
Type type() const
Definition widgets.hpp:2496
Type
Definition widgets.hpp:2490
@ image_upload
Definition widgets.hpp:2490
@ search
Definition widgets.hpp:2490
@ send
Definition widgets.hpp:2490
@ close
Definition widgets.hpp:2490
Icon(Type t)
Definition widgets.hpp:2493
void set_type(Type t)
Definition widgets.hpp:2495
Definition image_cache.hpp:55
void set_fill_value(float value)
Value-driven silhouette fill (design-import shape-fill, e.g.
Definition widgets.hpp:2550
bool has_fill() const
Definition widgets.hpp:2559
const std::string & image_path() const
Definition widgets.hpp:2518
ImageCache * image_cache() const
Definition widgets.hpp:2542
void set_fill_color(canvas::Color color)
Definition widgets.hpp:2555
void set_image_cache(ImageCache *cache)
Attach an ImageCache.
Definition widgets.hpp:2541
const std::vector< canvas::Color > & fill_gradient() const
Definition widgets.hpp:2574
const std::string & image_source() const
Definition widgets.hpp:2535
void paint(canvas::Canvas &canvas) override
ImageView()
Definition widgets.hpp:2511
void set_image_path(const std::string &path)
Legacy API: filesystem path.
Definition widgets.hpp:2515
void set_image_source(const std::string &uri)
URI-keyed image source.
Definition widgets.hpp:2524
float fill_value() const
Definition widgets.hpp:2554
bool has_fill_gradient() const
Definition widgets.hpp:2575
void set_fill_gradient(std::vector< canvas::Color > stops)
Per-shape gradient fill (design-import shape-fill enhancement).
Definition widgets.hpp:2570
std::function< void(double)> on_change
Definition widgets.hpp:2811
void on_mouse_down(Point pos) override
void set_range(double min, double max)
Definition widgets.hpp:2802
const CaretBlinkConfig & caret_blink() const
Definition widgets.hpp:2826
void set_caret_blink(const CaretBlinkConfig &config)
Blink timing. Defaults to the process-wide config.
Definition widgets.hpp:2822
double value() const
Definition widgets.hpp:2801
void set_value(double v)
Definition widgets.hpp:2800
bool enabled() const
Definition widgets.hpp:2806
void on_frame_clock_changed() override
Re-home the caret-blink subscription when the tree's FrameClock changes — same dangling-cached-clock ...
void set_decimals(int d)
Definition widgets.hpp:2804
InlineValueEditor()
Definition widgets.hpp:2798
void set_enabled(bool e)
Definition widgets.hpp:2805
void set_suffix(std::string s)
Definition widgets.hpp:2803
bool editing() const
Definition widgets.hpp:2807
void on_text_input(const TextInputEvent &event) override
Text input from keyboard/IME (separate from key events).
void set_caret_style(CaretStyle style)
Caret shape. Defaults to the process-wide style (CaretStyle::ibeam).
Definition widgets.hpp:2818
void on_focus_changed(bool gained) override
Called when this view gains or loses focus.
bool on_key_event(const KeyEvent &event) override
Key event with modifiers and up/down state.
void paint(canvas::Canvas &canvas) override
CaretStyle caret_style() const
Definition widgets.hpp:2819
bool invalid() const
Definition widgets.hpp:2808
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2838
void set_captured_indicator_outline(canvas::Color color, float width_px)
Optional authored CSS border around a captured HTML pointer.
Definition widgets.hpp:1304
bool has_modulated_value() const
Definition widgets.hpp:1144
bool show_value() const
Definition widgets.hpp:1031
void set_captured_raster_origin(float x, float y, float scale)
A browser crop records an exact device-pixel origin and DPR.
Definition widgets.hpp:1267
static constexpr float end_angle
Definition widgets.hpp:1061
float captured_raster_origin_y() const
Definition widgets.hpp:1274
float captured_indicator_outline_width() const
Definition widgets.hpp:1309
std::pair< float, float > modulation_range(size_t ring) const
bool has_sprite_core() const
Definition widgets.hpp:1262
void set_label(std::string text)
Definition widgets.hpp:1008
void set_render_style(WidgetRenderStyle s)
Definition widgets.hpp:955
double real_value() const
Definition widgets.hpp:932
void set_sprite_core(float x, float y, float w, float h)
Opaque-core rectangle of the sprite body frame, in the FRAME's own pixel space (x,...
Definition widgets.hpp:1258
void set_show_label(bool show)
Definition widgets.hpp:1016
void set_format(std::function< std::string(float)> fmt)
Definition widgets.hpp:1036
std::function< void(int ring, float lo, float hi)> on_modulation_change
Definition widgets.hpp:1162
float value() const
Definition widgets.hpp:977
void set_modulation_phase(float p)
Definition widgets.hpp:1095
float captured_indicator_phase_rad() const
Definition widgets.hpp:1298
void advance_animations(float dt) override
Advance any per-widget non-CSS animation state (hover glows, thumb tweens, spinner phase) by dt secon...
const std::shared_ptr< SpriteStrip > & sprite_strip() const
Definition widgets.hpp:1248
void set_default_value(float v)
Definition widgets.hpp:979
static constexpr float start_angle
Definition widgets.hpp:1060
void set_current_value(double v) override
Set the value (if editable).
Definition widgets.hpp:941
SliderCore & slider()
Definition widgets.hpp:921
const SliderCore & slider() const
Definition widgets.hpp:922
const std::string & label() const
Definition widgets.hpp:1014
void set_captured_indicator(float r_in, float r_out, float width_px, canvas::Color color, float phase_rad=0.0f, bool color_authored=true)
Geometry of the design's OWN pointer (e.g.
Definition widgets.hpp:1285
const std::vector< ModulationRing > & modulation_rings() const
Definition widgets.hpp:1089
bool is_gesture_active() const override
True while the user is actively manipulating this widget's value with a pointer drag (knob turn,...
Definition widgets.hpp:1183
double get_maximum_value() const override
Maximum value.
Definition widgets.hpp:947
float lottie_time() const
Definition widgets.hpp:1243
void set_sprite_strip(std::shared_ptr< SpriteStrip > strip)
Sprite strip: designer-created filmstrip for knob appearance.
Definition widgets.hpp:1247
void on_mouse_up(Point pos) override
void set_widget_schema(std::string json)
Definition widgets.hpp:1068
double get_current_value() const override
Current value as a number.
Definition widgets.hpp:940
void paint(canvas::Canvas &canvas) override
float captured_raster_origin_x() const
Definition widgets.hpp:1273
void set_lottie_time(float t)
Definition widgets.hpp:1242
std::string get_value_string() const override
Prefer the widget's own display formatter ("-6.0 dB") over the generic percentage — it is the string ...
Definition widgets.hpp:950
double get_minimum_value() const override
Minimum value.
Definition widgets.hpp:946
void set_lottie_json(std::string json)
Definition widgets.hpp:1240
const std::string & widget_schema() const
Definition widgets.hpp:1069
void set_value(float v)
Definition widgets.hpp:968
bool dragging_modulation() const
Definition widgets.hpp:1164
void set_skew(float s)
Skew / response curve (see RangeSlider::set_skew).
Definition widgets.hpp:990
float captured_indicator_r_out() const
Definition widgets.hpp:1297
bool wants_wheel_value() const override
True if this widget adjusts its VALUE on a scroll-wheel over it (knobs, faders, sliders,...
Definition widgets.hpp:1167
float position_for_value() const
Definition widgets.hpp:998
bool set_value(float v, Notify notify)
Notifying overload.
Definition widgets.hpp:975
float modulated_value(size_t ring) const
void on_mouse_enter() override
void set_modulation_rings(std::vector< ModulationRing > rings)
Definition widgets.hpp:1085
void on_mouse_event(const MouseEvent &event) override
Mouse down with full event context.
void set_show_value(bool show)
Controls only the formatter text painted inside the dial.
Definition widgets.hpp:1026
float hover_glow() const
Definition widgets.hpp:1056
float modulation_phase() const
Definition widgets.hpp:1100
void on_wheel(float delta_y) override
Adjust the widget's value by a scroll-wheel delta (delta_y positive = scrolled down).
Definition widgets.hpp:1168
bool has_captured_indicator() const
Definition widgets.hpp:1295
float value_for_position(float p) const
Definition widgets.hpp:1002
canvas::Color captured_indicator_outline_color() const
Definition widgets.hpp:1310
bool show_label() const
Definition widgets.hpp:1021
void on_mouse_down(Point pos) override
Knob()
Definition widgets.hpp:903
float modulated_display_value() const
The played value while one is set, else value().
Definition widgets.hpp:1148
bool set_real_value(double v, Notify notify=Notify::sync)
Write in real-world units. Snapped to the interval, clamped to the range.
Definition widgets.hpp:934
void set_modulated_value(float normalized)
Definition widgets.hpp:1130
std::function< void()> on_gesture_end
Definition widgets.hpp:1044
std::function< void()> on_gesture_begin
Definition widgets.hpp:1043
float captured_indicator_r_in() const
Definition widgets.hpp:1296
void clear_modulated_value()
Definition widgets.hpp:1138
void on_mouse_drag(Point pos) override
const std::string & lottie_json() const
Definition widgets.hpp:1241
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:1053
float skew() const
Definition widgets.hpp:991
void on_mouse_leave() override
float captured_raster_scale() const
Definition widgets.hpp:1272
std::function< void(float)> on_change
Called when the value changes (from user interaction or programmatic).
Definition widgets.hpp:1042
bool captured_indicator_color_authored() const
Definition widgets.hpp:1299
void set_skew_from_midpoint(float mid_normalized)
mid_normalized is in the knob's NORMALIZED space (see value()), so this call means the same thing wit...
Definition widgets.hpp:994
WidgetRenderStyle render_style() const
Definition widgets.hpp:956
void set_range(double min_value, double max_value, double interval=0.0)
Real-world range and quantization interval.
Definition widgets.hpp:926
float default_value() const
Definition widgets.hpp:983
const std::string & text() const
Definition widgets.hpp:67
TextTransform text_transform() const
Definition widgets.hpp:233
static LineBreakPathCounts line_break_path_counts()
float measured_height(float available_width) const
Width-aware height: shaper-true line count × line-height for a multi_line Label given the parent's av...
canvas::Color text_decoration_color() const
Definition widgets.hpp:240
bool selection_highlight(int &start_utf8, int &end_utf8) const override
The current highlight. Returns false when nothing is highlighted.
TextDecorationStyle
CSS text-decoration-style: solid, double, dotted, dashed, wavy.
Definition widgets.hpp:249
@ double_
Definition widgets.hpp:249
@ dashed
Definition widgets.hpp:249
@ wavy
Definition widgets.hpp:249
@ dotted
Definition widgets.hpp:249
@ solid
Definition widgets.hpp:249
LabelAlign text_align() const
Definition widgets.hpp:179
Rect own_text_box() const
Layout slot for this Label's own text when it also has element children.
Definition widgets.hpp:261
SelectionPolicy selection_policy() const
Definition widgets.hpp:548
bool is_selectable() const
Resolved answer for this Label right now.
Definition widgets.hpp:561
bool multi_line() const
Definition widgets.hpp:194
float max_content_width() const
Widest this label can be if nothing soft-wrapped — CSS max-content.
float intrinsic_width() const override
Intrinsic width — measured text width.
TextDecoration
CSS text-decoration: none, underline, line-through, overline.
Definition widgets.hpp:236
@ overline
Definition widgets.hpp:236
@ none
Definition widgets.hpp:236
@ line_through
Definition widgets.hpp:236
@ underline
Definition widgets.hpp:236
Label()
Definition widgets.hpp:58
EditTrigger editable() const
Definition widgets.hpp:490
void set_own_text_box(Rect box)
Definition widgets.hpp:263
SelectableLayout selectable_layout() const override
Where the glyphs landed at the last paint. See SelectableLayout.
Definition widgets.hpp:572
void set_multi_line(bool ml)
Definition widgets.hpp:188
EditTrigger
Definition widgets.hpp:480
@ none
not editable
Definition widgets.hpp:481
@ single_click
Definition widgets.hpp:482
@ double_click
Definition widgets.hpp:483
int font_style() const
Definition widgets.hpp:146
void set_font_size(float size)
Definition widgets.hpp:108
void clear_own_text_box()
Definition widgets.hpp:264
Label(std::string text)
Definition widgets.hpp:59
std::size_t attributed_span_count() const
Definition widgets.hpp:89
void set_text_decoration_color(canvas::Color c)
Definition widgets.hpp:239
void clear_attributed_string()
Definition widgets.hpp:82
int font_weight() const
Definition widgets.hpp:136
void set_selection_highlight(int start_utf8, int end_utf8) override
Paint this byte range as selected. start >= end clears the highlight.
std::function< void(const std::string &text)> on_text_change
Fires when an edit is COMMITTED with new text (not on every keystroke, and not at all when the edit i...
Definition widgets.hpp:494
void hide_editor(bool commit)
Close the inline editor. commit false discards the edit.
float measured_width(float available_width) const
Width-aware painted width: how far this Label's glyphs actually reach at available_width,...
bool has_own_text_box() const
Definition widgets.hpp:262
bool has_own_text_align() const
Definition widgets.hpp:180
void set_editable(EditTrigger trigger)
Definition widgets.hpp:486
bool has_text_decoration_color() const
Definition widgets.hpp:241
int effective_font_weight() const
The CSS numeric weight this Label will actually be painted at — its own when it was given one,...
int line_clamp() const
Definition widgets.hpp:223
void set_line_clamp(int n)
CSS line-clamp / -webkit-line-clamp.
Definition widgets.hpp:222
void set_font_smoothing(canvas::FontSmoothing smoothing)
CSS -webkit-font-smoothing this text was authored with.
Definition widgets.hpp:160
void set_text_color(canvas::Color c)
Definition widgets.hpp:184
bool has_own_font_weight() const
Definition widgets.hpp:137
bool soft_wraps() const
Whether this Label soft-wraps once CSS white-space inheritance is applied: it is multi-line AND no an...
Definition widgets.hpp:208
TextDecoration text_decoration() const
Definition widgets.hpp:238
TextDecorationStyle text_decoration_style() const
Definition widgets.hpp:251
void set_text(std::string text)
PaintedTextExtents painted_text_extents(float available_width) const
float baseline_y() const
Baseline offset from the top of the Label's measured box, used by Yoga's YGNodeSetBaselineFunc to hon...
float intrinsic_height() const override
Intrinsic height based on font size and line height.
void set_attributed_string(canvas::AttributedString a)
Definition widgets.hpp:72
std::string_view selectable_text() const override
The widget's text, in UTF-8.
Definition widgets.hpp:569
SelectionPolicy
Whether this Label takes part in text selection.
Definition widgets.hpp:539
@ inherit
Definition widgets.hpp:539
@ never
Definition widgets.hpp:539
@ always
Definition widgets.hpp:539
void set_text_transform(TextTransform t)
Definition widgets.hpp:227
void set_cached_line_boxes(std::vector< CachedLineBox > boxes, float basis_width, std::string basis_face, bool wrap_on_cache_miss=false)
Adopt a browser's own line breaking for this text.
float line_height() const
Definition widgets.hpp:176
void set_font_family(std::string family)
CSS font-family string (e.g.
Definition widgets.hpp:120
void set_vertical_align(canvas::TextVerticalAlign a)
Set vertical text alignment within the label's bounds.
Definition widgets.hpp:890
void set_text_decoration_style(TextDecorationStyle s)
Definition widgets.hpp:250
void set_text_direction(canvas::TextDirection d)
Set text direction (LTR, RTL, vertical top-to-bottom, vertical bottom-to-top).
Definition widgets.hpp:886
void clear_cached_line_boxes()
Drop browser-captured line geometry when a responsive layout intentionally changes typography.
Definition widgets.hpp:394
static void reset_line_break_path_counts()
SelectableText * as_selectable_text() override
This view's painted text can join a cross-widget selection; null for the vast majority of views,...
Definition widgets.hpp:523
void show_editor()
Open the inline editor now, regardless of the click trigger.
std::function< void()> on_editor_hide
Definition widgets.hpp:499
TextTransform
CSS text-transform: uppercase, lowercase, capitalize, none.
Definition widgets.hpp:226
@ uppercase
Definition widgets.hpp:226
@ none
Definition widgets.hpp:226
@ capitalize
Definition widgets.hpp:226
@ lowercase
Definition widgets.hpp:226
canvas::TextVerticalAlign vertical_align() const
Definition widgets.hpp:891
const std::vector< CachedLineBox > & cached_line_boxes() const
Definition widgets.hpp:406
void set_selection_policy(SelectionPolicy policy)
Definition widgets.hpp:540
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:513
std::string effective_font_family() const
The font family this Label paints with: its own font_family() if set, else the nearest ancestor's inh...
canvas::TextDirection text_direction() const
Definition widgets.hpp:887
void set_text_decoration(TextDecoration d)
Definition widgets.hpp:237
void paint(canvas::Canvas &canvas) override
float letter_spacing() const
Definition widgets.hpp:155
const std::string & font_family() const
Definition widgets.hpp:126
void on_mouse_down(Point pos) override
void set_letter_spacing(float sp)
Definition widgets.hpp:148
void set_cached_line_metrics(LineMetrics metrics)
The primary font's ascent and descent the browser laid the cached lines out with.
bool has_own_letter_spacing() const
Definition widgets.hpp:156
bool has_own_font_size() const
Definition widgets.hpp:116
canvas::FontSmoothing font_smoothing() const
Definition widgets.hpp:166
std::function< void()> on_editor_show
Fires when the inline editor opens / closes, for owners that need to suppress a hover highlight or a ...
Definition widgets.hpp:498
float font_size() const
Definition widgets.hpp:115
bool has_attributed_string() const
Definition widgets.hpp:88
bool editor_active() const
Definition widgets.hpp:507
void set_text_align(LabelAlign align)
Definition widgets.hpp:178
TextEditMetrics text_edit_metrics(canvas::Canvas &canvas, std::string_view edit_text) const
View * selectable_view() override
The View this capability is mixed into. Never null.
Definition widgets.hpp:577
void set_font_style(int style)
Definition widgets.hpp:140
bool has_own_text_color() const
Definition widgets.hpp:185
canvas::Color text_color() const
Definition widgets.hpp:186
void set_font_weight(int weight)
Definition widgets.hpp:129
bool captured_wrap_fallback() const
Definition widgets.hpp:409
bool inherits_nowrap() const
True when this Label's own mode is normal and its nearest ancestor with a non-normal mode is nowrap.
void set_line_height(float lh)
Definition widgets.hpp:170
TextEditor * editor() const
The live inline editor, or null.
Definition widgets.hpp:510
Panel()
Definition widgets.hpp:2683
float border_width() const
Definition widgets.hpp:2695
const std::string & background_token() const
Definition widgets.hpp:2692
void set_border_width(float w)
Definition widgets.hpp:2690
const std::string & border_token() const
Definition widgets.hpp:2693
void set_border_token(std::string token)
Definition widgets.hpp:2686
void set_background_token(std::string token)
Definition widgets.hpp:2685
void set_corner_radius(float r)
Definition widgets.hpp:2689
float corner_radius() const
Definition widgets.hpp:2694
void paint(canvas::Canvas &canvas) override
void set_track_thickness(float t)
Track thickness in pixels (default 5).
Definition widgets.hpp:1952
void set_color_scheme(ColorScheme scheme)
The page colour scheme the chromium_refresh skin resolves its palette from (CSS color-scheme).
Definition widgets.hpp:1899
float position_for_value() const
Track position [0,1] (skew-mapped) the thumb is drawn at for the value.
Definition widgets.hpp:1967
void on_mouse_leave() override
UaSkin Skin
Which control skin paints the slider.
Definition widgets.hpp:1884
Skin skin() const
Definition widgets.hpp:1893
Orientation
Definition widgets.hpp:1752
@ horizontal
Definition widgets.hpp:1752
@ vertical
Definition widgets.hpp:1752
bool has_accent_color() const
Definition widgets.hpp:1877
double get_minimum_value() const override
Minimum value.
Definition widgets.hpp:1781
float max_value() const
Definition widgets.hpp:1796
std::function< void()> on_gesture_begin
Fired when an interactive drag begins / ends, so a binding can bracket the edit in a host automation ...
Definition widgets.hpp:1976
float modulated_display_value() const
The played fraction of [min, max] while one is set.
Definition widgets.hpp:1851
void set_skin(Skin s)
Definition widgets.hpp:1887
void set_value(float v, Notify notify=Notify::sync)
Set the current value.
Definition widgets.hpp:1818
void set_skew_from_midpoint(float mid_value)
Choose skew so mid_value lands at the middle of the track (position 0.5).
ChromiumRefreshParts chromium_refresh_parts(float device_scale=1.0f) const
chromium_refresh_parts() for this slider's current box, value, orientation, state,...
void set_current_value(double v) override
Set the value (if editable).
Definition widgets.hpp:1776
bool wants_wheel_value() const override
True if this widget adjusts its VALUE on a scroll-wheel over it (knobs, faders, sliders,...
Definition widgets.hpp:1856
std::function< void(float)> on_change
Fired when the value changes — from drag, click, or set_value().
Definition widgets.hpp:1972
void set_min(float v)
Inclusive lower bound (default 0).
Definition widgets.hpp:1790
static ChromiumRefreshParts chromium_refresh_parts(Rect bounds, float position, bool vertical, ControlState state, ColorScheme scheme, std::optional< canvas::Color > accent=std::nullopt, float device_scale=1.0f)
Pure layout + palette of the chromium_refresh skin for a box of bounds (local coordinates) with the t...
void set_orientation(Orientation o)
Definition widgets.hpp:1866
void on_mouse_enter() override
bool is_gesture_active() const override
True while the user is actively manipulating this widget's value with a pointer drag (knob turn,...
Definition widgets.hpp:1864
void set_step(float v)
Step size for quantisation.
Definition widgets.hpp:1802
float min_value() const
Definition widgets.hpp:1791
UaColorScheme ColorScheme
Definition widgets.hpp:1885
static constexpr float kMinorAxisExtent
The thumb's minor-axis extent at rest, in points.
Definition widgets.hpp:1772
float step() const
Definition widgets.hpp:1803
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:1991
void set_accent_color(canvas::Color c)
Override the accent color for the active fill.
Definition widgets.hpp:1872
void set_skew(float s)
Skew / response curve, shaping the mapping between track position and value.
Definition widgets.hpp:1961
ControlState control_state() const
The state the chromium_refresh palette resolves for this slider now: disabled when not enabled,...
canvas::Color accent_color() const
Definition widgets.hpp:1878
double get_maximum_value() const override
Maximum value.
Definition widgets.hpp:1782
double get_step_size() const override
Step increment for keyboard navigation.
Definition widgets.hpp:1783
float track_thickness() const
Definition widgets.hpp:1953
RangeSlider()
Definition widgets.hpp:1754
void on_mouse_event(const MouseEvent &event) override
Mouse down with full event context.
float value() const
Definition widgets.hpp:1824
float skew() const
Definition widgets.hpp:1962
void paint(canvas::Canvas &canvas) override
ColorScheme color_scheme() const
Definition widgets.hpp:1905
void on_wheel(float delta_y) override
Adjust the widget's value by a scroll-wheel delta (delta_y positive = scrolled down).
Definition widgets.hpp:1857
void set_max(float v)
Inclusive upper bound (default 1).
Definition widgets.hpp:1795
void clear_accent_color()
Definition widgets.hpp:1876
double get_current_value() const override
Current value as a number.
Definition widgets.hpp:1775
void set_modulated_value(float normalized)
Display-only played value while a modulator moves this parameter around value() (same contract as Kno...
Definition widgets.hpp:1832
UaControlState ControlState
Definition widgets.hpp:1886
void clear_modulated_value()
Definition widgets.hpp:1841
float intrinsic_height() const override
The control's own default box height.
Definition widgets.hpp:1986
Orientation orientation() const
Definition widgets.hpp:1867
void on_mouse_drag(Point pos) override
bool has_modulated_value() const
Definition widgets.hpp:1847
void advance_animations(float dt) override
Advance any per-widget non-CSS animation state (hover glows, thumb tweens, spinner phase) by dt secon...
Definition widgets.hpp:1992
float hover_scale() const
Definition widgets.hpp:1993
std::function< void()> on_gesture_end
Definition widgets.hpp:1977
A widget whose painted text can join a document-level selection.
Definition selectable_text.hpp:58
Definition slider_core.hpp:77
double normalized() const
LINEAR position of the value in the range, in [0,1], with NO response curve applied.
Definition slider_core.hpp:135
bool set_normalized(double n, Notify notify=Notify::sync)
Definition slider_core.hpp:139
Text editing widget with comprehensive keyboard/mouse interaction.
Definition text_editor.hpp:47
std::optional< float > font_size_override() const
Definition widgets.hpp:2456
const std::optional< canvas::Color > & on_border_color_override() const
Definition widgets.hpp:2453
void set_designed_overlay(bool enabled)
Marks a browser-captured control as a live replacement.
Definition widgets.hpp:2459
void set_on(bool v)
Definition widgets.hpp:2393
void on_mouse_down(Point pos) override
void set_on_background_color(canvas::Color color)
Definition widgets.hpp:2431
const std::optional< canvas::Color > & on_text_color_override() const
Definition widgets.hpp:2451
ToggleButton()
Definition widgets.hpp:2385
bool designed_overlay() const
Definition widgets.hpp:2460
void set_on_text_color(canvas::Color color)
Definition widgets.hpp:2433
void set_off_background_color(canvas::Color color)
Definition widgets.hpp:2432
void set_radio_group(int id)
Radio-group id (0 = independent toggle, the default).
Definition widgets.hpp:2446
void set_font_size(float size)
Definition widgets.hpp:2438
void set_on_border_color(canvas::Color color)
Definition widgets.hpp:2435
const std::optional< canvas::Color > & on_background_color_override() const
Definition widgets.hpp:2449
void paint(canvas::Canvas &canvas) override
std::optional< float > corner_radius_override() const
Definition widgets.hpp:2455
const std::optional< canvas::Color > & off_border_color_override() const
Definition widgets.hpp:2454
void set_label(std::string text)
Definition widgets.hpp:2423
const std::string & label() const
Definition widgets.hpp:2429
const std::optional< canvas::Color > & off_background_color_override() const
Definition widgets.hpp:2450
void set_off_border_color(canvas::Color color)
Definition widgets.hpp:2436
std::function< void(bool)> on_toggle
Definition widgets.hpp:2462
int radio_group() const
Definition widgets.hpp:2447
bool is_on() const
Definition widgets.hpp:2421
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2466
const std::optional< canvas::Color > & off_text_color_override() const
Definition widgets.hpp:2452
void set_corner_radius(float radius)
Definition widgets.hpp:2437
float intrinsic_height() const override
Definition widgets.hpp:2468
void set_off_text_color(canvas::Color color)
Definition widgets.hpp:2434
bool set_on(bool v, Notify notify)
Notifying overload.
Definition widgets.hpp:2407
std::function< void(bool)> on_toggle
Called when the toggle state changes.
Definition widgets.hpp:2072
void on_mouse_enter() override
float lottie_time() const
Definition widgets.hpp:2094
void set_label(std::string text)
Definition widgets.hpp:2065
const std::string & label() const
Definition widgets.hpp:2069
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2078
void advance_animations(float dt) override
Advance any per-widget non-CSS animation state (hover glows, thumb tweens, spinner phase) by dt secon...
void on_mouse_leave() override
const std::string & widget_schema() const
Definition widgets.hpp:2090
const std::string & lottie_json() const
Definition widgets.hpp:2092
bool set_on(bool v, Notify notify, bool animate=true)
Notifying overload.
void on_mouse_down(Point pos) override
void set_lottie_json(std::string json)
Definition widgets.hpp:2091
void set_widget_schema(std::string json)
Definition widgets.hpp:2089
float hover_opacity() const
Definition widgets.hpp:2082
Toggle()
Definition widgets.hpp:2038
void set_on(bool v, bool animate=true)
Set the on/off state.
void set_lottie_time(float t)
Definition widgets.hpp:2093
bool designed_overlay() const
Definition widgets.hpp:2086
void set_designed_overlay(bool enabled)
Definition widgets.hpp:2085
bool is_on() const
Definition widgets.hpp:2054
float thumb_position() const
Definition widgets.hpp:2081
void paint(canvas::Canvas &canvas) override
Lightweight, embeddable value animator for widget members.
Definition animation.hpp:293
Definition view.hpp:73
void request_repaint_self(float halo=4.0f)
Invalidate only THIS view's own rect, overscanned by halo so a glow, focus ring or modulation arc pai...
void set_hit_slop(float uniform)
Definition view.hpp:764
Position position() const
Definition view.hpp:1767
void request_repaint()
Request that this view's host invalidate and schedule a repaint.
void set_border_radius(float r)
Definition view.hpp:1202
const std::string & id() const
Definition view.hpp:1059
void set_cursor(CursorStyle c)
Definition view.hpp:2760
@ slider
Definition view.hpp:989
@ toggle
Definition view.hpp:989
@ checkbox
Definition view.hpp:990
@ radio
Definition view.hpp:990
@ button
Definition view.hpp:990
@ label
Definition view.hpp:989
@ group
Definition view.hpp:989
float corner_radius() const
Definition view.hpp:1183
bool visible() const
Definition view.hpp:333
void set_derived_access_label(std::string label)
The CONTENT-DERIVED accessible name: a widget's visible text (Label::set_text, Knob/Fader/Toggleset_l...
Definition view.hpp:1020
void mark_runtime_view_kind(RuntimeViewKind kind)
Definition view.hpp:2379
@ default_
Definition view.hpp:2742
@ text
Definition view.hpp:2742
void invalidate_layout()
Mark layout as needing recalculation (auto-invalidation).
Definition view.hpp:898
int animate(std::function< void(float)> apply, float from, float to, float duration_s, std::function< float(float)> ease={}, std::function< void()> on_done={}, const std::string &tag={})
float top() const
Definition view.hpp:1798
void set_access_role(AccessRole role)
Definition view.hpp:995
const std::shared_ptr< WidgetMetrics > & metrics() const
Definition view.hpp:251
void set_access_checked(std::string s)
Definition view.hpp:1044
bool enabled() const
CSS :disabled equivalent — blocks input, reduces opacity.
Definition view.hpp:715
void set_focusable(bool f)
Definition view.hpp:669
Rect bounds() const
Definition view.hpp:84
float scale() const
Definition view.hpp:1902
void paint(canvas::Canvas &canvas) override
State
Definition widgets.hpp:2877
@ captured
Definition widgets.hpp:2877
@ armed
Definition widgets.hpp:2877
@ recording
Definition widgets.hpp:2877
void on_mouse_drag(Point pos) override
void set_waveform(std::vector< float > samples)
Normalized waveform samples (-1..1) drawn across the main area.
Definition widgets.hpp:2893
void set_threshold(float threshold)
Capture threshold (0..1) marked by the draggable meter thumb.
Definition widgets.hpp:2910
void set_state(State s, Notify notify=Notify::sync)
Transport state.
std::function< void(float)> on_threshold_change
Definition widgets.hpp:2917
void on_mouse_down(Point pos) override
State state() const
Definition widgets.hpp:2889
WaveformRecorder()
Definition widgets.hpp:2879
void set_level(float level)
Live input level (0..1) shown by the bottom meter fill.
Definition widgets.hpp:2900
std::function< void()> on_play
Definition widgets.hpp:2922
const std::vector< float > & waveform() const
Definition widgets.hpp:2897
std::function< void()> on_record
Optional transport-action callbacks, fired alongside the state advance.
Definition widgets.hpp:2920
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2928
std::function< void(State)> on_state_change
Definition widgets.hpp:2890
float threshold() const
Definition widgets.hpp:2916
void on_mouse_up(Point pos) override
float level() const
Definition widgets.hpp:2906
std::function< void()> on_stop
Definition widgets.hpp:2921
void on_mouse_up(Point pos) override
float y_value() const
Definition widgets.hpp:2606
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition widgets.hpp:2648
void set_y(float v)
Definition widgets.hpp:2604
bool is_gesture_active() const override
True while the user is actively manipulating this widget's value with a pointer drag (knob turn,...
Definition widgets.hpp:2651
void on_mouse_down(Point pos) override
bool has_modulated_value() const
Definition widgets.hpp:2626
void set_x_label(std::string l)
Definition widgets.hpp:2637
float modulated_display_y() const
Definition widgets.hpp:2633
void clear_modulated_x()
Definition widgets.hpp:2620
void set_modulated_y(float normalized)
Definition widgets.hpp:2617
float modulated_display_x() const
The played position on each axis while one is set, else the base.
Definition widgets.hpp:2630
void set_modulated_x(float normalized)
Display-only played position on either axis while a modulator moves that parameter around the base (s...
Definition widgets.hpp:2614
float x_value() const
Definition widgets.hpp:2605
void set_x(float v)
Definition widgets.hpp:2603
std::function< void(float, float)> on_change
Definition widgets.hpp:2640
void clear_modulated_y()
Definition widgets.hpp:2623
void set_y_label(std::string l)
Definition widgets.hpp:2638
void paint(canvas::Canvas &canvas) override
void on_mouse_drag(Point pos) override
std::function< void()> on_gesture_end
Definition widgets.hpp:2642
std::function< void()> on_gesture_begin
Definition widgets.hpp:2641
Single authoritative time source for the view system.
Device-appropriate target sizing shared by pointer-driven hit tests.
Definition affine_transform.hpp:24
TextVerticalAlign
Definition canvas.hpp:222
@ top
Definition canvas.hpp:222
FontSmoothing
CSS -webkit-font-smoothing as it affects glyph rasterization on macOS.
Definition text_raster_policy.hpp:40
@ auto_smoothing
Definition text_raster_policy.hpp:41
TextDirection
Definition canvas.hpp:231
@ left_to_right
Definition canvas.hpp:231
BreakMode
CSS overflow-wrap / word-break break opportunity mode.
Definition text_shaper.hpp:162
@ normal
Definition text_shaper.hpp:163
Device capability vocabulary: one tier ladder, one thermal ladder, and the projection from observed i...
Definition child_process.hpp:15
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ path
Definition anchor_strategy.hpp:42
void queue_async_notification(std::function< void()> fn)
Queue a callback for the next flush.
CaretStyle default_caret_style() noexcept
Process-wide caret defaults.
View * enclosing_text_selection_region(View &from)
The nearest enclosing text-content region of from (itself included), or null when it is not inside on...
@ normal
Definition design_ir.hpp:450
@ right
Definition border_style.hpp:31
@ left
Definition border_style.hpp:31
UaControlState
Interaction state a chromium_refresh palette is keyed on.
Definition widgets.hpp:1731
@ disabled
Definition widgets.hpp:1731
@ pressed
Definition widgets.hpp:1731
@ hovered
Definition widgets.hpp:1731
LabelAlign
Text alignment for Label.
Definition widgets.hpp:54
@ left
Definition widgets.hpp:54
@ justify
Definition widgets.hpp:54
@ match_parent
Definition widgets.hpp:54
UaSkin
Which skin a form-control widget paints with.
Definition widgets.hpp:1725
@ chromium_refresh
Definition widgets.hpp:1725
CaretStyle
Caret shape. All three anchor at the same caret x.
Definition caret.hpp:63
@ light
Definition appearance_tracker.hpp:11
@ dark
Definition appearance_tracker.hpp:11
@ touch
Direct touch (finger).
Definition input_events.hpp:77
UaColorScheme ua_scheme_for_accent(canvas::Color accent, UaColorScheme scheme, canvas::Color contrast_light, canvas::Color contrast_dark)
The scheme Chromium actually paints a control in when an author accent is set: it flips scheme to the...
Notify
Whether a value write notifies listeners, and when.
Definition slider_core.hpp:60
@ async
fire listeners at the next flush_async_notifications()
Definition slider_core.hpp:63
@ none
change the value silently
Definition slider_core.hpp:61
@ sync
fire listeners before returning
Definition slider_core.hpp:62
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
float fader_chart_track_thickness(float cross_extent)
Height of the stadium a Fader's chart is drawn in, given the fader's extent across its track: half of...
Definition widgets.hpp:1713
@ s
Definition input_events.hpp:41
@ a
Definition input_events.hpp:39
@ h
Definition input_events.hpp:39
@ l
Definition input_events.hpp:40
@ y
Definition input_events.hpp:42
@ c
Definition input_events.hpp:39
@ r
Definition input_events.hpp:41
@ n
Definition input_events.hpp:40
@ d
Definition input_events.hpp:39
@ p
Definition input_events.hpp:40
@ k
Definition input_events.hpp:40
@ b
Definition input_events.hpp:39
@ x
Definition input_events.hpp:41
@ v
Definition input_events.hpp:41
@ o
Definition input_events.hpp:40
@ e
Definition input_events.hpp:39
@ t
Definition input_events.hpp:41
@ w
Definition input_events.hpp:41
WidgetRenderStyle
Rendering style for widgets.
Definition data_display.hpp:20
@ standard
Definition data_display.hpp:20
@ auto_
Definition geometry.hpp:370
@ baseline
Definition geometry.hpp:370
@ fader
Definition view.hpp:47
@ toggle
Definition view.hpp:47
@ knob
Definition view.hpp:47
@ color
Definition css_animation.hpp:121
@ height
Definition css_animation.hpp:128
@ width
Definition css_animation.hpp:127
UaColorScheme
The CSS color-scheme a chromium_refresh palette resolves against.
Definition widgets.hpp:1728
@ center
Definition design_ir.hpp:57
std::optional< canvas::Canvas::SDFShape > widget_chart_shape(const View &view)
The SDF shape a widget's chart shader is drawn on: flat_arc for a Knob's value arc,...
STL namespace.
The value engine shared by Pulp's continuous controls.
Definition canvas.hpp:52
static constexpr Color rgba(float r, float g, float b, float a=1.0f)
Construct from float channels [0,1] (>1.0 allowed for HDR).
Definition canvas.hpp:56
Everything the skin paints, in local coordinates, in paint order: the lighten layer (disabled only) a...
Definition widgets.hpp:2342
std::optional< canvas::Color > lighten
Definition widgets.hpp:2349
canvas::Color border
Definition widgets.hpp:2351
float ring_width
Definition widgets.hpp:2355
float radius
corner radius of surface and both rings
Definition widgets.hpp:2347
Rect outer_ring
stroke centreline of the outer ring
Definition widgets.hpp:2353
Rect inner_ring
stroke centreline of the inner ring
Definition widgets.hpp:2354
bool solid
A box under 5 device px a side is one solid fill rect on bounds.
Definition widgets.hpp:2344
canvas::Color fill
Definition widgets.hpp:2350
canvas::Color inner_ring_color
Definition widgets.hpp:2357
Rect surface
bounds inset half the border width
Definition widgets.hpp:2346
Rect bounds
Definition widgets.hpp:2345
float border_width
Definition widgets.hpp:2348
canvas::Color outer_ring_color
Definition widgets.hpp:2356
bool focus_ring
Definition widgets.hpp:2352
Blink timing. The defaults are the values Pulp has always used.
Definition caret.hpp:78
Everything the chromium_refresh skin paints, in local coordinates, in paint order: lighten (disabled ...
Definition widgets.hpp:2231
float check_width
Definition widgets.hpp:2244
ColorScheme scheme
palette used, after the accent flip
Definition widgets.hpp:2234
canvas::Color background
Definition widgets.hpp:2237
Rect box
the largest centred square in the bounds
Definition widgets.hpp:2232
std::array< Point, 3 > check
Definition widgets.hpp:2243
std::optional< canvas::Color > lighten
disabled lighten layer on background_rect
Definition widgets.hpp:2235
canvas::Color mark
check / dash colour, or the radio dot colour
Definition widgets.hpp:2241
std::optional< Rect > dash
indeterminate dash, rounded by radius
Definition widgets.hpp:2245
std::optional< canvas::Color > border
stroked 1px on box inset 0.5px
Definition widgets.hpp:2238
std::optional< Rect > dot
checked radio dot (box inset 20%)
Definition widgets.hpp:2246
Rect background_rect
box inset 0.2px
Definition widgets.hpp:2236
Rect border_rect
Definition widgets.hpp:2239
float radius
2px for a checkbox, half the side for a radio
Definition widgets.hpp:2233
std::optional< canvas::Color > accent_fill
filled on box
Definition widgets.hpp:2240
bool has_check
polyline check[0..2], check_width wide
Definition widgets.hpp:2242
static constexpr float default_min_target_pt(PointerType type) noexcept
Minimum comfortable target extent for a pointer type, in points.
Definition hit_metrics.hpp:33
Rich key event with key code, modifiers, and repeat state.
Definition input_events.hpp:193
Definition widgets.hpp:1075
canvas::Color color
per-source color
Definition widgets.hpp:1083
float lo
low-end offset from base (−1..1)
Definition widgets.hpp:1081
float hi
high-end offset from base (−1..1)
Definition widgets.hpp:1082
One captured line of text: where it sits in this Label's box, and which slice of the text it holds (U...
Definition widgets.hpp:349
float top
Definition widgets.hpp:351
int start
Definition widgets.hpp:354
int length
Definition widgets.hpp:355
float width
Definition widgets.hpp:352
float height
Definition widgets.hpp:353
float left
Definition widgets.hpp:350
How many Labels took each line-breaking path since the last reset.
Definition widgets.hpp:366
int cached
captured layout used verbatim
Definition widgets.hpp:367
int reflowed
captured layout present but rejected
Definition widgets.hpp:368
int uncached
no captured layout at all
Definition widgets.hpp:369
The extents of the glyphs this Label will actually paint, given the content width it has been laid ou...
Definition widgets.hpp:334
float height
total painted height (line clamp applied)
Definition widgets.hpp:337
float width
widest painted line
Definition widgets.hpp:336
int line_count
Definition widgets.hpp:338
bool measured
Definition widgets.hpp:335
Rect ink
Local box of the pixels paint() covers.
Definition widgets.hpp:343
WYSIWYG text-edit metrics — the geometry an inspector edit overlay needs to draw a caret + selection ...
Definition widgets.hpp:458
float band_height
Definition widgets.hpp:462
float local_text_left
local x where text starts (handles center/right align)
Definition widgets.hpp:460
std::vector< float > caret_x_by_byte
size == display_text.size()+1
Definition widgets.hpp:463
float local_band_y
local y of the caret/selection band (top of text)
Definition widgets.hpp:461
std::string display_text
Definition widgets.hpp:459
A text run's primary-font ascent and descent in CSS px, as the browser rounded and adjusted them when...
Definition design_line_metrics.hpp:13
Rich mouse/pointer event with position, modifiers, button state, pointer ID, and stylus properties.
Definition input_events.hpp:104
Definition geometry.hpp:20
Everything the chromium_refresh skin paints, in local coordinates.
Definition widgets.hpp:1912
ColorScheme scheme
palette used, after the accent flip
Definition widgets.hpp:1920
Rect track
rounded track, shortened one device pixel per end
Definition widgets.hpp:1913
float border_radius
corner radius of border
Definition widgets.hpp:1917
Rect thumb
the circle's bounds (16px part inset half a device pixel)
Definition widgets.hpp:1919
Rect value_clip
clip for the value bar; the thumb covers its open end
Definition widgets.hpp:1915
Rect border
track inset by half the border width
Definition widgets.hpp:1916
canvas::Color value_fill
Definition widgets.hpp:1922
canvas::Color track_fill
Definition widgets.hpp:1921
float border_width
Definition widgets.hpp:1918
canvas::Color border_color
Definition widgets.hpp:1923
float track_radius
corner radius of track (half its thickness)
Definition widgets.hpp:1914
canvas::Color thumb_fill
Definition widgets.hpp:1924
Axis-aligned rectangle in continuous (float) coordinates.
Definition geometry.hpp:52
The painted line geometry of one widget.
Definition selectable_text.hpp:52
Text input from IME or keyboard composition (separate from KeyEvent).
Definition input_events.hpp:210
Source-compatible spelling of <pulp/dataplane/value_source.hpp>.
A pluggable paint delegate for Pulp's stock widgets.