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
|
||||
// chrome, text) share the same colors as the mobile UI and respond to light/dark/user themes.
|
||||
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) ===
|
||||
// The canvas records raster keyframe ids whose `raw_pixels` weren't resident
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ use crate::panes::{NodePath, SharedPaneState};
|
|||
use crate::RenderContext;
|
||||
|
||||
const GRAB_H: f32 = 16.0;
|
||||
const HEAD_H: f32 = 30.0;
|
||||
const CHIP_H: f32 = 30.0;
|
||||
const HEAD_H: f32 = 44.0;
|
||||
|
||||
fn inspector_path() -> NodePath {
|
||||
vec![MOBILE_NS, 200]
|
||||
|
|
@ -123,61 +122,54 @@ pub fn render(
|
|||
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 = egui::Rect::from_min_max(
|
||||
egui::pos2(rect.left(), head_y),
|
||||
egui::pos2(rect.right(), head_y + HEAD_H),
|
||||
);
|
||||
ui.painter().text(
|
||||
egui::pos2(head.left() + 14.0, head.center().y),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
title(&rc.shared),
|
||||
egui::FontId::proportional(13.0),
|
||||
pal.text,
|
||||
let title_text = title(&rc.shared);
|
||||
let mut jump: Option<(usize, usize)> = None;
|
||||
let mut do_close = false;
|
||||
let mut hui = ui.new_child(
|
||||
egui::UiBuilder::new()
|
||||
.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));
|
||||
let cresp = ui.interact(close, ui.id().with("mobile_inspector_close"), egui::Sense::click());
|
||||
ui.painter().text(
|
||||
close.center(),
|
||||
egui::Align2::CENTER_CENTER,
|
||||
super::icons::X,
|
||||
super::icons::font(16.0),
|
||||
if cresp.hovered() { pal.text } else { pal.text_dim },
|
||||
);
|
||||
if cresp.clicked() {
|
||||
hui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui
|
||||
.add(egui::Button::new(egui::RichText::new(super::icons::X).font(super::icons::font(18.0))).frame(false))
|
||||
.clicked()
|
||||
{
|
||||
do_close = true;
|
||||
}
|
||||
ui.add_space(4.0);
|
||||
// Reversed so the visual order stays Timeline, then Nodes.
|
||||
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.focus = FocusSelection::None;
|
||||
}
|
||||
|
||||
// Jump-to chips (reframe to a related surface, keeping the selection).
|
||||
let chip_y = head.bottom();
|
||||
let mut cx = rect.left() + 12.0;
|
||||
for (i, chip) in CHIPS.iter().enumerate() {
|
||||
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;
|
||||
if let Some((top, count)) = jump {
|
||||
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.
|
||||
let content = egui::Rect::from_min_max(
|
||||
egui::pos2(rect.left(), chip_y + CHIP_H),
|
||||
egui::pos2(rect.left(), head.bottom()),
|
||||
rect.max,
|
||||
);
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Any non-empty value other than "0" enables it.
|
||||
pub fn is_mobile_env() -> bool {
|
||||
|
|
|
|||
Loading…
Reference in New Issue