Touch-friendly egui widgets on mobile; egui-based inspector header
Add mobile::apply_touch_style(ctx) — applied every frame when mobile is active — enlarging egui's global spacing/sizing (interact_size, button padding, slider width/rail, combo width/height, icon sizes, scrollbar, body/button text) so the standard widgets in panes and dialogs are finger-sized. Desktop is untouched. Rework the mobile inspector header into a single row using real egui widgets: title (left, truncating) + Timeline/Nodes jump buttons + ✕ close, laid out with egui layouts so they inherit the touch sizing and theme visuals instead of being hand-painted.
This commit is contained in:
parent
943ff7c15f
commit
4b2802076b
|
|
@ -5346,6 +5346,10 @@ impl eframe::App for EditorApp {
|
||||||
// Apply the theme's palette to egui's global visuals so the standard widgets (dialogs, pane
|
// Apply the theme's palette to egui's global visuals so the standard widgets (dialogs, pane
|
||||||
// chrome, text) share the same colors as the mobile UI and respond to light/dark/user themes.
|
// chrome, text) share the same colors as the mobile UI and respond to light/dark/user themes.
|
||||||
self.theme.apply_to_egui(ctx);
|
self.theme.apply_to_egui(ctx);
|
||||||
|
// On mobile, enlarge egui spacing/sizing so widgets in panes/dialogs are touch-friendly.
|
||||||
|
if self.mobile_active() {
|
||||||
|
mobile::apply_touch_style(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// === Raster fault-in (Phase 3 paging) ===
|
// === Raster fault-in (Phase 3 paging) ===
|
||||||
// The canvas records raster keyframe ids whose `raw_pixels` weren't resident
|
// The canvas records raster keyframe ids whose `raw_pixels` weren't resident
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ use crate::panes::{NodePath, SharedPaneState};
|
||||||
use crate::RenderContext;
|
use crate::RenderContext;
|
||||||
|
|
||||||
const GRAB_H: f32 = 16.0;
|
const GRAB_H: f32 = 16.0;
|
||||||
const HEAD_H: f32 = 30.0;
|
const HEAD_H: f32 = 44.0;
|
||||||
const CHIP_H: f32 = 30.0;
|
|
||||||
|
|
||||||
fn inspector_path() -> NodePath {
|
fn inspector_path() -> NodePath {
|
||||||
vec![MOBILE_NS, 200]
|
vec![MOBILE_NS, 200]
|
||||||
|
|
@ -123,61 +122,54 @@ pub fn render(
|
||||||
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeVertical);
|
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeVertical);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header row: title + close.
|
// Single header row (egui widgets — they inherit the mobile touch sizing + theme visuals):
|
||||||
|
// title on the left, [Timeline] [Nodes] jump buttons + ✕ close on the right.
|
||||||
let head_y = rect.top() + GRAB_H;
|
let head_y = rect.top() + GRAB_H;
|
||||||
let head = egui::Rect::from_min_max(
|
let head = egui::Rect::from_min_max(
|
||||||
egui::pos2(rect.left(), head_y),
|
egui::pos2(rect.left(), head_y),
|
||||||
egui::pos2(rect.right(), head_y + HEAD_H),
|
egui::pos2(rect.right(), head_y + HEAD_H),
|
||||||
);
|
);
|
||||||
ui.painter().text(
|
let title_text = title(&rc.shared);
|
||||||
egui::pos2(head.left() + 14.0, head.center().y),
|
let mut jump: Option<(usize, usize)> = None;
|
||||||
egui::Align2::LEFT_CENTER,
|
let mut do_close = false;
|
||||||
title(&rc.shared),
|
let mut hui = ui.new_child(
|
||||||
egui::FontId::proportional(13.0),
|
egui::UiBuilder::new()
|
||||||
pal.text,
|
.max_rect(head.shrink2(egui::vec2(12.0, 5.0)))
|
||||||
|
.layout(egui::Layout::left_to_right(egui::Align::Center)),
|
||||||
);
|
);
|
||||||
let close = egui::Rect::from_min_size(egui::pos2(head.right() - 36.0, head.top()), egui::vec2(36.0, HEAD_H));
|
hui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
let cresp = ui.interact(close, ui.id().with("mobile_inspector_close"), egui::Sense::click());
|
if ui
|
||||||
ui.painter().text(
|
.add(egui::Button::new(egui::RichText::new(super::icons::X).font(super::icons::font(18.0))).frame(false))
|
||||||
close.center(),
|
.clicked()
|
||||||
egui::Align2::CENTER_CENTER,
|
{
|
||||||
super::icons::X,
|
do_close = true;
|
||||||
super::icons::font(16.0),
|
}
|
||||||
if cresp.hovered() { pal.text } else { pal.text_dim },
|
ui.add_space(4.0);
|
||||||
);
|
// Reversed so the visual order stays Timeline, then Nodes.
|
||||||
if cresp.clicked() {
|
for chip in CHIPS.iter().rev() {
|
||||||
|
if ui.button(chip.label).clicked() {
|
||||||
|
jump = Some(chip.window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Title fills the remaining space on the left.
|
||||||
|
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
|
||||||
|
ui.add(egui::Label::new(egui::RichText::new(title_text).color(pal.text)).truncate());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if do_close {
|
||||||
rc.shared.selection.clear();
|
rc.shared.selection.clear();
|
||||||
*rc.shared.focus = FocusSelection::None;
|
*rc.shared.focus = FocusSelection::None;
|
||||||
}
|
}
|
||||||
|
if let Some((top, count)) = jump {
|
||||||
// Jump-to chips (reframe to a related surface, keeping the selection).
|
state.window_top = top;
|
||||||
let chip_y = head.bottom();
|
state.window_count = count;
|
||||||
let mut cx = rect.left() + 12.0;
|
state.weights = [1.0, 1.0, 1.0];
|
||||||
for (i, chip) in CHIPS.iter().enumerate() {
|
state.anim = None;
|
||||||
let galley = ui.painter().layout_no_wrap(
|
|
||||||
chip.label.to_string(),
|
|
||||||
egui::FontId::proportional(11.0),
|
|
||||||
pal.text,
|
|
||||||
);
|
|
||||||
let w = galley.size().x + 18.0;
|
|
||||||
let chip_rect = egui::Rect::from_min_size(egui::pos2(cx, chip_y + 4.0), egui::vec2(w, CHIP_H - 8.0));
|
|
||||||
let resp = ui.interact(chip_rect, ui.id().with(("mobile_inspector_chip", i)), egui::Sense::click());
|
|
||||||
ui.painter().rect_filled(chip_rect, 11.0, if resp.hovered() { pal.line } else { pal.surface_alt });
|
|
||||||
ui.painter().rect_stroke(chip_rect, 11.0, egui::Stroke::new(1.0, pal.line), egui::StrokeKind::Inside);
|
|
||||||
ui.painter().galley(chip_rect.center() - galley.size() * 0.5, galley, pal.text);
|
|
||||||
if resp.clicked() {
|
|
||||||
let (top, count) = chip.window;
|
|
||||||
state.window_top = top;
|
|
||||||
state.window_count = count;
|
|
||||||
state.weights = [1.0, 1.0, 1.0];
|
|
||||||
state.anim = None;
|
|
||||||
}
|
|
||||||
cx += w + 6.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Properties content — reuse the Infopanel full-bleed.
|
// Properties content — reuse the Infopanel full-bleed.
|
||||||
let content = egui::Rect::from_min_max(
|
let content = egui::Rect::from_min_max(
|
||||||
egui::pos2(rect.left(), chip_y + CHIP_H),
|
egui::pos2(rect.left(), head.bottom()),
|
||||||
rect.max,
|
rect.max,
|
||||||
);
|
);
|
||||||
if content.height() > 1.0 {
|
if content.height() > 1.0 {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,36 @@ pub fn dialog_width(ctx: &egui::Context, desired: f32) -> f32 {
|
||||||
desired.min(avail.max(200.0))
|
desired.min(avail.max(200.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enlarge egui's spacing/sizing so the standard widgets (buttons, dropdowns, sliders, text fields)
|
||||||
|
/// in panes and dialogs are touch-friendly. Applied every frame after the theme visuals when the
|
||||||
|
/// mobile shell is active. The mobile chrome (transport, omnibutton, headers) is hand-sized already;
|
||||||
|
/// this targets the egui-widget content inside panes.
|
||||||
|
pub fn apply_touch_style(ctx: &egui::Context) {
|
||||||
|
use egui::{FontId, TextStyle};
|
||||||
|
ctx.style_mut(|s| {
|
||||||
|
let sp = &mut s.spacing;
|
||||||
|
// Minimum touch target height for buttons/sliders/checkboxes.
|
||||||
|
sp.interact_size = egui::vec2(sp.interact_size.x.max(44.0), 38.0);
|
||||||
|
sp.button_padding = egui::vec2(12.0, 9.0);
|
||||||
|
sp.item_spacing = egui::vec2(10.0, 10.0);
|
||||||
|
sp.slider_width = 200.0;
|
||||||
|
sp.slider_rail_height = 10.0;
|
||||||
|
sp.combo_width = 200.0;
|
||||||
|
sp.combo_height = 360.0;
|
||||||
|
sp.text_edit_width = 220.0;
|
||||||
|
sp.icon_width = 24.0;
|
||||||
|
sp.icon_width_inner = 14.0;
|
||||||
|
sp.icon_spacing = 8.0;
|
||||||
|
sp.scroll.bar_width = 16.0;
|
||||||
|
sp.menu_margin = egui::Margin::same(8);
|
||||||
|
// Larger text for touch legibility.
|
||||||
|
s.text_styles.insert(TextStyle::Body, FontId::proportional(15.0));
|
||||||
|
s.text_styles.insert(TextStyle::Button, FontId::proportional(15.0));
|
||||||
|
s.text_styles.insert(TextStyle::Monospace, FontId::monospace(13.0));
|
||||||
|
s.text_styles.insert(TextStyle::Small, FontId::proportional(12.0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the mobile UI is requested via the `LB_MOBILE_UI` env var.
|
/// Returns true if the mobile UI is requested via the `LB_MOBILE_UI` env var.
|
||||||
/// Any non-empty value other than "0" enables it.
|
/// Any non-empty value other than "0" enables it.
|
||||||
pub fn is_mobile_env() -> bool {
|
pub fn is_mobile_env() -> bool {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue