refactor(editor): route settings close through shared feedback

This commit is contained in:
Kayshen-X 2026-06-14 03:20:07 +08:00
parent 52d75cbaec
commit 42029dabc8
10 changed files with 121 additions and 9 deletions

View file

@ -0,0 +1,8 @@
//! Canonical pressed-state targets for the agent-settings modal.
/// `EditorUiState.pressed_button` target for plain settings-modal
/// buttons that use shared ghost button feedback.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AgentSettingsButton {
Close,
}

View file

@ -15,4 +15,5 @@ pub enum ButtonPressTarget {
Git(crate::git_button_state::GitButton),
ExportDialog(crate::export_dialog_state::ExportDialogButton),
FigmaImport(crate::figma_import_state::FigmaImportButton),
AgentSettings(crate::agent_settings_button_state::AgentSettingsButton),
}

View file

@ -10,6 +10,7 @@
pub mod agent_indicators;
pub mod agent_settings;
pub mod agent_settings_builtin_presets;
pub mod agent_settings_button_state;
pub mod agent_settings_connection;
pub mod align;
pub mod align_guides;
@ -140,6 +141,7 @@ pub use agent_settings_builtin_presets::{
builtin_agent_preset, infer_builtin_agent_preset, normalize_builtin_agent_preset,
BuiltinAgentPreset, BuiltinAgentPresetKey, BUILTIN_AGENT_PRESETS,
};
pub use agent_settings_button_state::AgentSettingsButton;
pub use align::AlignAction;
pub use button_press_state::ButtonPressTarget;
pub use chat::{

View file

@ -23,6 +23,7 @@ use op_editor_core::agent_settings::{
use op_editor_core::editor_ui_state::EditorUiState;
use op_editor_core::BuiltinAgentPresetKey;
use op_editor_core::EditorState;
use op_editor_core::{AgentSettingsButton, ButtonPressTarget};
pub const PANEL_WIDTH: f32 = 720.0;
pub const PANEL_HEIGHT: f32 = 720.0;
@ -464,7 +465,7 @@ fn paint_panel(
}
}
cx.backend.restore();
paint_close(cx, theme, settings, panel);
paint_close(cx, theme, settings, _ui, panel);
}
fn paint_sidebar(
@ -536,11 +537,22 @@ fn paint_sidebar(
}
}
fn paint_close(cx: &mut PaintCx<'_>, theme: &Theme, settings: &AgentSettings, panel: Rect) {
fn paint_close(
cx: &mut PaintCx<'_>,
theme: &Theme,
settings: &AgentSettings,
ui: &EditorUiState,
panel: Rect,
) {
let close = close_rect(panel);
if settings.hover_agent_settings_close {
cx.backend.fill_round_rect(close, 6.0, theme.button_hover);
}
let pressed = ui.button_pressed(ButtonPressTarget::AgentSettings(AgentSettingsButton::Close));
crate::widgets::button::paint_ghost_button_feedback(
cx.backend,
theme,
close,
settings.hover_agent_settings_close,
pressed,
);
draw_icon(
cx.backend,
Icon::Close,

View file

@ -6,7 +6,7 @@ use op_editor_core::agent_settings::{
AcpAgentField, AgentSettingsTab, BuiltinAgentField, ImageSearchField, ImageTestStatus,
SettingsFocus,
};
use op_editor_core::EditorState;
use op_editor_core::{AgentSettingsButton, ButtonPressTarget, EditorState};
#[derive(Default)]
struct CaptureBackend {
@ -139,6 +139,37 @@ fn close_button_hover_paints_visible_wash() {
);
}
#[test]
fn pressed_close_button_uses_shared_button_feedback() {
let mut state = EditorState::default();
state.editor_ui.pressed_button =
Some(ButtonPressTarget::AgentSettings(AgentSettingsButton::Close));
let panel = AgentSettingsPanel::for_editor(&state);
let rect = panel.rect(1200.0, 800.0);
let mut backend = CaptureBackend::default();
let mut cx = PaintCx {
backend: &mut backend,
};
panel.paint(&mut cx, rect);
let close = Rect {
origin: Point2D::new(rect.origin.x + rect.size.x - 32.0, rect.origin.y + 16.0),
size: Point2D::new(16.0, 16.0),
};
let expected = panel
.theme
.button_hover
.with_alpha(panel.theme.button_hover.a * 1.8);
assert!(
backend
.round_fills
.iter()
.any(|(fill, color)| *fill == close && color_eq(*color, expected)),
"pressed close button should paint the shared pressed feedback token"
);
}
#[test]
fn agents_nav_icon_uses_ts_pen_glyph_not_pencil() {
const PEN_PATH: &str =

View file

@ -93,6 +93,19 @@ pub fn figma_import_button(
}
}
/// Map a widget-layer `AgentSettingsHit` onto the canonical
/// `op_editor_core::AgentSettingsButton` for shared pressed feedback.
pub fn agent_settings_button(
hit: crate::widgets::agent_settings_panel::AgentSettingsHit,
) -> Option<op_editor_core::AgentSettingsButton> {
use crate::widgets::agent_settings_panel::AgentSettingsHit as W;
use op_editor_core::AgentSettingsButton as O;
match hit {
W::Close => Some(O::Close),
_ => None,
}
}
/// Map a widget-layer `ExportDialogHit` onto the canonical
/// `op_editor_core::ExportDialogButton` for the modal export dialog's
/// hover wash. The `Format` arm reuses [`export_format`] to canonicalise

View file

@ -3,7 +3,7 @@ use op_editor_core::agent_settings::{
AcpAgentField, AgentSettingsTab, BuiltinAgentField, ImageGenField, ImageGenProvider,
ImageSearchField, ImageTestStatus, SettingsFocus,
};
use op_editor_core::BuiltinAgentPresetKey;
use op_editor_core::{AgentSettingsButton, BuiltinAgentPresetKey, ButtonPressTarget};
use op_editor_ui::widgets::agent_settings_panel::AgentSettingsPanel;
fn agent_settings_content_metrics(host: &WidgetHostNative) -> (f32, f32, f32) {
@ -24,6 +24,24 @@ fn acp_card_y(content_y: f32) -> f32 {
acp_header_y(content_y) + 28.0 + 28.0
}
#[test]
fn close_press_sets_and_release_clears_agent_settings_button() {
let mut host = WidgetHostNative::new();
let panel = AgentSettingsPanel::for_editor(host.editor_state());
let rect = panel.rect(1200.0, 800.0);
let close_x = rect.origin.x + rect.size.x - 24.0;
let close_y = rect.origin.y + 24.0;
assert!(host.dispatch_agent_settings_press(close_x, close_y, 1200.0, 800.0));
assert_eq!(
host.editor_state().editor_ui.pressed_button,
Some(ButtonPressTarget::AgentSettings(AgentSettingsButton::Close))
);
assert!(host.apply_release_with_viewport(1200.0, 800.0));
assert_eq!(host.editor_state().editor_ui.pressed_button, None);
}
#[test]
fn builtin_agent_api_key_focus_accepts_text_and_rebuilds_models() {
let mut host = WidgetHostNative::new();

View file

@ -59,7 +59,11 @@ impl WidgetHostNative {
let panel = AgentSettingsPanel::for_editor(&self.editor_state);
let panel_rect = panel.rect(vw, vh);
let point = Point2D::new(x, y);
match panel.hit_test(panel_rect, point) {
let hit = panel.hit_test(panel_rect, point);
self.editor_state.editor_ui.pressed_button =
op_editor_ui::widgets::editor_state_ext::agent_settings_button(hit)
.map(op_editor_core::ButtonPressTarget::AgentSettings);
match hit {
AgentSettingsHit::Close | AgentSettingsHit::Outside => {
self.commit_settings_focus_if_any();
self.editor_state.editor_ui.agent_settings_open = false;

View file

@ -25,7 +25,11 @@ impl WidgetHost {
};
let panel = AgentSettingsPanel::for_editor(&self.editor_state);
let panel_rect = panel.rect(vw, vh);
match panel.hit_test(panel_rect, Point2D::new(x, y)) {
let hit = panel.hit_test(panel_rect, Point2D::new(x, y));
self.editor_state.editor_ui.pressed_button =
op_editor_ui::widgets::editor_state_ext::agent_settings_button(hit)
.map(op_editor_core::ButtonPressTarget::AgentSettings);
match hit {
AgentSettingsHit::Close | AgentSettingsHit::Outside => {
self.commit_settings_focus();
self.editor_state.editor_ui.agent_settings_open = false;

View file

@ -3,8 +3,27 @@ use op_editor_core::agent_settings::{
AgentSettingsTab, BuiltinAgentField, ImageGenField, ImageGenProvider, ImageTestStatus,
SettingsFocus,
};
use op_editor_core::{AgentSettingsButton, ButtonPressTarget};
use op_editor_ui::widgets::agent_settings_panel::AgentSettingsPanel;
#[test]
fn close_press_sets_and_release_clears_agent_settings_button() {
let mut host = WidgetHost::new();
let panel = AgentSettingsPanel::for_editor(&host.editor_state);
let rect = panel.rect(1200.0, 800.0);
let close_x = rect.origin.x + rect.size.x - 24.0;
let close_y = rect.origin.y + 24.0;
assert!(host.dispatch_agent_settings_press(close_x, close_y, 1200.0, 800.0));
assert_eq!(
host.editor_state.editor_ui.pressed_button,
Some(ButtonPressTarget::AgentSettings(AgentSettingsButton::Close))
);
assert!(host.apply_release_with_viewport(1200.0, 800.0));
assert_eq!(host.editor_state.editor_ui.pressed_button, None);
}
#[test]
fn toggling_builtin_kind_commits_focused_api_key_draft() {
let mut host = WidgetHost::new();