refactor(editor): route variables panel buttons through shared feedback
This commit is contained in:
parent
931361be0b
commit
ace706830f
|
|
@ -14,6 +14,7 @@ pub enum ButtonPressTarget {
|
|||
ChatFooter(crate::chat_button_state::ChatFooterButton),
|
||||
Git(crate::git_button_state::GitButton),
|
||||
PropertyPanel(usize),
|
||||
VariablesPanel(crate::variables_panel_state::VariablesPanelButton),
|
||||
DesignMd(crate::design_md_button_state::DesignMdButton),
|
||||
ComponentBrowser(crate::component_browser_state::ComponentBrowserButton),
|
||||
ExportDialog(crate::export_dialog_state::ExportDialogButton),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::widgets::{LayoutBox, LayoutCx, PaintCx, Widget, WidgetId};
|
|||
use crate::{Point2D, Rect};
|
||||
use jian_ops_schema::variable::{VariableKind, VariableScalar, VariableValue};
|
||||
use op_editor_core::editor_ui_state::VariableRowFocus;
|
||||
use op_editor_core::{EditorState, Locale, VariablesPanelButton};
|
||||
use op_editor_core::{ButtonPressTarget, EditorState, Locale, VariablesPanelButton};
|
||||
|
||||
mod geometry;
|
||||
mod header;
|
||||
|
|
@ -146,6 +146,7 @@ pub struct VariablesPanel {
|
|||
/// Open `⋯` row menu, keyed by UNFILTERED row index.
|
||||
row_menu_open: Option<usize>,
|
||||
hover: Option<VariablesPanelButton>,
|
||||
pressed: Option<VariablesPanelButton>,
|
||||
editing_name_row: Option<usize>,
|
||||
editing_value_cell: Option<(usize, usize)>,
|
||||
header_input: jian_core::text_input::TextInputState,
|
||||
|
|
@ -254,6 +255,10 @@ impl VariablesPanel {
|
|||
scroll: state.editor_ui.variables_scroll.offset,
|
||||
row_menu_open: state.editor_ui.variables_row_menu,
|
||||
hover: state.editor_ui.variables_panel_hover,
|
||||
pressed: match state.editor_ui.pressed_button {
|
||||
Some(ButtonPressTarget::VariablesPanel(button)) => Some(button),
|
||||
_ => None,
|
||||
},
|
||||
editing_name_row: state.editor_ui.variable_row_focus.and_then(|f| match f {
|
||||
VariableRowFocus::Name(i) => Some(i),
|
||||
VariableRowFocus::Number(_)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ mod tests {
|
|||
use crate::Color;
|
||||
use jian_ops_schema::variable::{VariableKind, VariableScalar};
|
||||
use op_editor_core::variables_panel_state::VariablesPanelButton;
|
||||
use op_editor_core::ButtonPressTarget;
|
||||
|
||||
#[derive(Default)]
|
||||
struct HoverBackend {
|
||||
|
|
@ -158,4 +159,36 @@ mod tests {
|
|||
"add-theme button should paint a hover wash"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pressed_header_targets_paint_shared_feedback() {
|
||||
let mut state = themed_state();
|
||||
state.editor_ui.pressed_button = Some(ButtonPressTarget::VariablesPanel(
|
||||
VariablesPanelButton::AddTheme,
|
||||
));
|
||||
let panel = VariablesPanel::for_editor(&state);
|
||||
assert_eq!(panel.pressed, Some(VariablesPanelButton::AddTheme));
|
||||
let rect = Rect {
|
||||
origin: Point2D::new(0.0, 0.0),
|
||||
size: Point2D::new(VARIABLES_PANEL_WIDTH, panel.intrinsic_height()),
|
||||
};
|
||||
let expected = panel
|
||||
.theme
|
||||
.button_hover
|
||||
.with_alpha(panel.theme.button_hover.a * 1.8);
|
||||
let mut backend = HoverBackend::default();
|
||||
let mut cx = PaintCx {
|
||||
backend: &mut backend,
|
||||
};
|
||||
|
||||
panel.paint(&mut cx, rect);
|
||||
|
||||
assert!(
|
||||
backend
|
||||
.round_fills
|
||||
.iter()
|
||||
.any(|(fill, color)| *fill == panel.add_theme_rect(rect) && *color == expected),
|
||||
"pressed add-theme button should paint shared pressed feedback"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use super::*;
|
||||
use crate::widgets::button::paint_button_feedback_wash;
|
||||
use crate::widgets::property_panel_text_input::paint_text_input_view_value;
|
||||
use crate::widgets::{draw_icon, Icon, PaintCx};
|
||||
use crate::{Color, Point2D, Rect};
|
||||
|
|
@ -13,6 +14,20 @@ const VALUE_INPUT_MIN_WIDTH: f32 = 96.0;
|
|||
const VALUE_INPUT_MAX_WIDTH: f32 = 160.0;
|
||||
const FOOTER_CHEVRON_LABEL_GAP: f32 = 12.0;
|
||||
|
||||
fn paint_feedback(
|
||||
panel: &VariablesPanel,
|
||||
cx: &mut PaintCx<'_>,
|
||||
target: VariablesPanelButton,
|
||||
rect: Rect,
|
||||
radius: f32,
|
||||
) {
|
||||
let hovered = panel.hover == Some(target);
|
||||
let pressed = panel.pressed == Some(target);
|
||||
if hovered || pressed {
|
||||
paint_button_feedback_wash(cx.backend, &panel.theme, rect, radius, hovered, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn paint_panel(panel: &VariablesPanel, cx: &mut PaintCx<'_>, rect: Rect) {
|
||||
let theme = panel.theme;
|
||||
let labels = panel.labels();
|
||||
|
|
@ -117,10 +132,13 @@ fn paint_theme_header(panel: &VariablesPanel, cx: &mut PaintCx<'_>, rect: Rect)
|
|||
let active_axis = panel.active_axis_label();
|
||||
for (idx, axis) in panel.theme_tab_labels().iter().enumerate() {
|
||||
let is_active = *axis == active_axis;
|
||||
if panel.hover == Some(VariablesPanelButton::ThemeTab(idx)) {
|
||||
cx.backend
|
||||
.fill_round_rect(panel.theme_tab_rect(rect, idx), 8.0, theme.button_hover);
|
||||
}
|
||||
paint_feedback(
|
||||
panel,
|
||||
cx,
|
||||
VariablesPanelButton::ThemeTab(idx),
|
||||
panel.theme_tab_rect(rect, idx),
|
||||
8.0,
|
||||
);
|
||||
let color = if is_active {
|
||||
theme.foreground
|
||||
} else {
|
||||
|
|
@ -160,10 +178,7 @@ fn paint_theme_header(panel: &VariablesPanel, cx: &mut PaintCx<'_>, rect: Rect)
|
|||
}
|
||||
|
||||
let add_theme = panel.add_theme_rect(rect);
|
||||
if panel.hover == Some(VariablesPanelButton::AddTheme) {
|
||||
cx.backend
|
||||
.fill_round_rect(add_theme, 8.0, theme.button_hover);
|
||||
}
|
||||
paint_feedback(panel, cx, VariablesPanelButton::AddTheme, add_theme, 8.0);
|
||||
draw_icon(
|
||||
cx.backend,
|
||||
Icon::Plus,
|
||||
|
|
@ -174,9 +189,7 @@ fn paint_theme_header(panel: &VariablesPanel, cx: &mut PaintCx<'_>, rect: Rect)
|
|||
);
|
||||
|
||||
let preset = panel.preset_rect(rect);
|
||||
if panel.hover == Some(VariablesPanelButton::PresetMenu) {
|
||||
cx.backend.fill_round_rect(preset, 8.0, theme.button_hover);
|
||||
}
|
||||
paint_feedback(panel, cx, VariablesPanelButton::PresetMenu, preset, 8.0);
|
||||
let preset_label = panel.labels().preset;
|
||||
let preset_label_size = 13.0;
|
||||
let preset_label_x = preset.origin.x + 29.0;
|
||||
|
|
@ -208,9 +221,7 @@ fn paint_theme_header(panel: &VariablesPanel, cx: &mut PaintCx<'_>, rect: Rect)
|
|||
);
|
||||
|
||||
let close = close_rect(rect);
|
||||
if panel.hover == Some(VariablesPanelButton::Close) {
|
||||
cx.backend.fill_round_rect(close, 8.0, theme.button_hover);
|
||||
}
|
||||
paint_feedback(panel, cx, VariablesPanelButton::Close, close, 8.0);
|
||||
draw_icon(
|
||||
cx.backend,
|
||||
Icon::Close,
|
||||
|
|
@ -243,13 +254,13 @@ fn paint_variant_header(
|
|||
let col_w = variant_column_width(rect, variants.len());
|
||||
for (idx, variant) in variants.iter().enumerate() {
|
||||
let x = value_x + col_w * idx as f32;
|
||||
if panel.hover == Some(VariablesPanelButton::VariantHeader(idx)) {
|
||||
cx.backend.fill_round_rect(
|
||||
panel.variant_header_rect(rect, idx),
|
||||
8.0,
|
||||
theme.button_hover,
|
||||
);
|
||||
}
|
||||
paint_feedback(
|
||||
panel,
|
||||
cx,
|
||||
VariablesPanelButton::VariantHeader(idx),
|
||||
panel.variant_header_rect(rect, idx),
|
||||
8.0,
|
||||
);
|
||||
if panel.renaming_variant.as_deref() == Some(*variant) {
|
||||
let input_state = panel.rename_text_input(RenameTarget::Variant(variant));
|
||||
let value = input_state.map(|input| input.text()).unwrap_or(variant);
|
||||
|
|
@ -286,10 +297,13 @@ fn paint_variant_header(
|
|||
);
|
||||
}
|
||||
}
|
||||
if panel.hover == Some(VariablesPanelButton::AddVariant) {
|
||||
cx.backend
|
||||
.fill_round_rect(add_variant_rect(rect), 8.0, theme.button_hover);
|
||||
}
|
||||
paint_feedback(
|
||||
panel,
|
||||
cx,
|
||||
VariablesPanelButton::AddVariant,
|
||||
add_variant_rect(rect),
|
||||
8.0,
|
||||
);
|
||||
draw_icon(
|
||||
cx.backend,
|
||||
Icon::Plus,
|
||||
|
|
@ -346,21 +360,21 @@ fn paint_rows(
|
|||
break;
|
||||
}
|
||||
let source = var.source_idx;
|
||||
let row_hovered = matches!(
|
||||
panel.hover,
|
||||
Some(VariablesPanelButton::Row(i)) if i == source
|
||||
) || matches!(
|
||||
panel.hover,
|
||||
Some(VariablesPanelButton::RowMenuButton(i)) if i == source
|
||||
);
|
||||
if row_hovered {
|
||||
cx.backend.fill_round_rect(
|
||||
let row_hovered = panel.hover == Some(VariablesPanelButton::Row(source))
|
||||
|| panel.hover == Some(VariablesPanelButton::RowMenuButton(source));
|
||||
let row_pressed = panel.pressed == Some(VariablesPanelButton::Row(source))
|
||||
|| panel.pressed == Some(VariablesPanelButton::RowMenuButton(source));
|
||||
if row_hovered || row_pressed {
|
||||
paint_button_feedback_wash(
|
||||
cx.backend,
|
||||
&theme,
|
||||
Rect {
|
||||
origin: Point2D::new(rect.origin.x + 8.0, y + 3.0),
|
||||
size: Point2D::new(rect.size.x - 16.0, ROW_HEIGHT - 6.0),
|
||||
},
|
||||
8.0,
|
||||
theme.button_hover,
|
||||
row_hovered,
|
||||
row_pressed,
|
||||
);
|
||||
}
|
||||
paint_variable_name_cell(panel, cx, rect, var, idx, y);
|
||||
|
|
@ -585,9 +599,7 @@ fn paint_footer(
|
|||
) {
|
||||
let theme = panel.theme;
|
||||
let button = add_variable_rect(rect);
|
||||
if panel.hover == Some(VariablesPanelButton::AddVariable) {
|
||||
cx.backend.fill_round_rect(button, 8.0, theme.button_hover);
|
||||
}
|
||||
paint_feedback(panel, cx, VariablesPanelButton::AddVariable, button, 8.0);
|
||||
let center_y = button.origin.y + button.size.y / 2.0;
|
||||
let icon_size = 16.0;
|
||||
let label_size = 14.0;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ impl WidgetHostNative {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
};
|
||||
self.editor_state.editor_ui.pressed_button = vars
|
||||
.hover_at(vars_rect, point)
|
||||
.map(op_editor_core::ButtonPressTarget::VariablesPanel);
|
||||
match hit {
|
||||
VariablesPanelHit::Resize(edge) => {
|
||||
// Edge press arms a resize drag; cursor moves write the
|
||||
|
|
|
|||
|
|
@ -4,11 +4,59 @@ use super::{
|
|||
};
|
||||
use jian_ops_schema::variable::{VariableKind, VariableScalar, VariableValue};
|
||||
use op_editor_core::editor_ui_state::VariableRowFocus;
|
||||
use op_editor_core::{ButtonPressTarget, VariablesPanelButton};
|
||||
use op_editor_ui::widgets::variables_panel::{VariablesPanel, VariablesPanelHit};
|
||||
use op_editor_ui::widgets::{TOOLBAR_WIDTH, TOP_BAR_HEIGHT};
|
||||
use op_editor_ui::Point2D;
|
||||
|
||||
const VIEWPORT_W: f32 = 1280.0;
|
||||
const VIEWPORT_H: f32 = 900.0;
|
||||
|
||||
fn point_for_hit(host: &WidgetHostNative, want: &VariablesPanelHit) -> (f32, f32) {
|
||||
let rect = host
|
||||
.variables_panel_rect(VIEWPORT_W, VIEWPORT_H)
|
||||
.expect("variables panel rect");
|
||||
let panel = VariablesPanel::for_editor(host.editor_state());
|
||||
let mut y = rect.origin.y;
|
||||
while y < rect.origin.y + rect.size.y {
|
||||
let mut x = rect.origin.x;
|
||||
while x < rect.origin.x + rect.size.x {
|
||||
if panel
|
||||
.hit_test(rect, Point2D::new(x, y))
|
||||
.is_some_and(|hit| &hit == want)
|
||||
{
|
||||
return (x, y);
|
||||
}
|
||||
x += 2.0;
|
||||
}
|
||||
y += 2.0;
|
||||
}
|
||||
panic!("no panel point maps to {want:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_panel_press_sets_and_release_clears_pressed_button() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut().editor_ui.variables_panel_open = true;
|
||||
assert!(host.editor_state_mut().create_variable(
|
||||
"spacing",
|
||||
VariableKind::Number,
|
||||
VariableScalar::Num(8.0),
|
||||
));
|
||||
|
||||
let (x, y) = point_for_hit(&host, &VariablesPanelHit::AddTheme);
|
||||
assert!(host.apply_press(x, y, VIEWPORT_W, VIEWPORT_H));
|
||||
assert_eq!(
|
||||
host.editor_state().editor_ui.pressed_button,
|
||||
Some(ButtonPressTarget::VariablesPanel(
|
||||
VariablesPanelButton::AddTheme
|
||||
))
|
||||
);
|
||||
|
||||
assert!(host.apply_release_with_viewport(VIEWPORT_W, VIEWPORT_H));
|
||||
assert_eq!(host.editor_state().editor_ui.pressed_button, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_panel_floats_next_to_toolbar_like_ts() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ impl WidgetHost {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
};
|
||||
self.editor_state.editor_ui.pressed_button = vars
|
||||
.hover_at(vars_rect, point)
|
||||
.map(op_editor_core::ButtonPressTarget::VariablesPanel);
|
||||
self.commit_property_family_focus_if_any();
|
||||
match hit {
|
||||
VariablesPanelHit::Resize(edge) => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
use super::WidgetHost;
|
||||
use jian_ops_schema::variable::{VariableKind, VariableScalar, VariableValue};
|
||||
use op_editor_core::editor_ui_state::VariableRowFocus;
|
||||
use op_editor_core::{own_bounds, NodeId, PropertyFocus};
|
||||
use op_editor_core::{own_bounds, ButtonPressTarget, NodeId, PropertyFocus, VariablesPanelButton};
|
||||
use op_editor_ui::widgets::variables_panel::{
|
||||
VariablesPanel, VariablesPanelHit, VariablesResizeEdge,
|
||||
};
|
||||
|
|
@ -118,6 +118,29 @@ fn themed_value_for<'a>(
|
|||
.map(|e| &e.value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_panel_press_sets_and_release_clears_pressed_button() {
|
||||
let mut host = WidgetHost::new();
|
||||
host.editor_state.editor_ui.variables_panel_open = true;
|
||||
assert!(host.editor_state.create_variable(
|
||||
"spacing",
|
||||
VariableKind::Number,
|
||||
VariableScalar::Num(8.0),
|
||||
));
|
||||
|
||||
let (x, y) = point_for_hit(&host, &VariablesPanelHit::AddTheme);
|
||||
assert!(host.apply_press(x, y, W, H));
|
||||
assert_eq!(
|
||||
host.editor_state.editor_ui.pressed_button,
|
||||
Some(ButtonPressTarget::VariablesPanel(
|
||||
VariablesPanelButton::AddTheme
|
||||
))
|
||||
);
|
||||
|
||||
assert!(host.apply_release_with_viewport(W, H));
|
||||
assert_eq!(host.editor_state.editor_ui.pressed_button, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn close_button_closes_floating_panel() {
|
||||
let mut host = two_variant_color_host();
|
||||
|
|
|
|||
Loading…
Reference in a new issue