refactor(editor): route acp connection button through shared feedback
This commit is contained in:
parent
fefb3a7be8
commit
7f74c750e2
|
|
@ -13,6 +13,7 @@ pub enum AgentSettingsButton {
|
|||
BuiltinCancelDraft,
|
||||
AcpEdit(usize),
|
||||
AcpRemove(usize),
|
||||
AcpConnection(usize),
|
||||
AcpSaveDraft,
|
||||
AcpCancelDraft,
|
||||
McpServer,
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ use crate::widgets::agent_settings_header_action::{
|
|||
header_action_rect, header_action_text_baseline_y, header_action_text_x,
|
||||
};
|
||||
use crate::widgets::agent_settings_i18n::t as t_settings;
|
||||
use crate::widgets::button::paint_ghost_button_feedback;
|
||||
use crate::widgets::button::{paint_ghost_button_feedback, tokens_from_theme};
|
||||
use crate::widgets::icons::{draw_icon, Icon};
|
||||
use crate::widgets::PaintCx;
|
||||
use crate::{Color, Point2D, Rect, TextLayout};
|
||||
use jian_widgets::components::button::{Button, ButtonVariant};
|
||||
use op_editor_core::agent_settings::{
|
||||
AcpAgentConfig, AcpAgentField, AcpConnectionType, AgentSettings, SettingsFocus,
|
||||
};
|
||||
|
|
@ -359,7 +360,7 @@ fn paint_compact_acp_card(
|
|||
)),
|
||||
);
|
||||
}
|
||||
paint_connection_button(cx, theme, ui, agent, card);
|
||||
paint_connection_button(cx, theme, ui, agent, index, card);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
|
@ -421,17 +422,35 @@ fn paint_connection_button(
|
|||
theme: &Theme,
|
||||
ui: &EditorUiState,
|
||||
agent: &AcpAgentConfig,
|
||||
index: usize,
|
||||
card: Rect,
|
||||
) {
|
||||
let btn = connection_button_rect(card);
|
||||
let enabled = agent.ready() || agent.connected;
|
||||
let bg = if agent.connected {
|
||||
theme.muted
|
||||
} else if enabled {
|
||||
theme.primary
|
||||
if agent.connected {
|
||||
cx.backend.fill_round_rect(btn, 6.0, theme.muted);
|
||||
cx.backend.stroke_round_rect(btn, 6.0, theme.border, 1.0);
|
||||
} else {
|
||||
theme.button_hover
|
||||
};
|
||||
Button {
|
||||
label: "",
|
||||
icon_d: None,
|
||||
variant: if enabled {
|
||||
ButtonVariant::Primary
|
||||
} else {
|
||||
ButtonVariant::Outline
|
||||
},
|
||||
enabled: true,
|
||||
hovered: !enabled,
|
||||
pressed: false,
|
||||
font_size: 12.0,
|
||||
}
|
||||
.paint(cx.backend, btn, &tokens_from_theme(theme));
|
||||
}
|
||||
if ui.button_pressed(ButtonPressTarget::AgentSettings(
|
||||
AgentSettingsButton::AcpConnection(index),
|
||||
)) {
|
||||
paint_ghost_button_feedback(cx.backend, theme, btn, false, true);
|
||||
}
|
||||
let fg = if agent.connected {
|
||||
Color {
|
||||
r: 0.93,
|
||||
|
|
@ -444,8 +463,6 @@ fn paint_connection_button(
|
|||
} else {
|
||||
theme.muted_foreground
|
||||
};
|
||||
cx.backend.fill_round_rect(btn, 6.0, bg);
|
||||
cx.backend.stroke_round_rect(btn, 6.0, theme.border, 1.0);
|
||||
let label = if agent.connected {
|
||||
t_settings(ui, "settings.agents.disconnect")
|
||||
} else if agent.ready() {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,17 @@ fn acp_remove_rect(panel: Rect) -> Rect {
|
|||
}
|
||||
}
|
||||
|
||||
fn acp_connection_rect(panel: Rect) -> Rect {
|
||||
let card = acp_card_rect_after_empty_builtin(panel);
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
card.origin.x + card.size.x - 12.0 - CONNECT_BTN_W,
|
||||
card.origin.y + (card.size.y - 28.0) / 2.0,
|
||||
),
|
||||
size: Point2D::new(CONNECT_BTN_W, 28.0),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pressed_builtin_compact_actions_use_shared_button_feedback() {
|
||||
for (button, expected_rect) in [
|
||||
|
|
@ -205,3 +216,36 @@ fn pressed_acp_compact_actions_use_shared_button_feedback() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pressed_acp_connection_button_uses_shared_button_feedback() {
|
||||
let mut state = EditorState::default();
|
||||
state.editor_ui.theme_mode = ThemeMode::Light;
|
||||
state.editor_ui.agent_settings.tab = AgentSettingsTab::Agents;
|
||||
state.editor_ui.agent_settings.add_acp_agent();
|
||||
state.editor_ui.agent_settings.acp_agents[0].command = "op-agent".into();
|
||||
state.editor_ui.pressed_button = Some(ButtonPressTarget::AgentSettings(
|
||||
AgentSettingsButton::AcpConnection(0),
|
||||
));
|
||||
let panel = AgentSettingsPanel::for_editor(&state);
|
||||
let rect = panel.rect(1200.0, 800.0);
|
||||
let target = acp_connection_rect(rect);
|
||||
let expected = panel
|
||||
.theme
|
||||
.button_hover
|
||||
.with_alpha(panel.theme.button_hover.a * 1.8);
|
||||
let mut backend = CaptureBackend::default();
|
||||
let mut cx = PaintCx {
|
||||
backend: &mut backend,
|
||||
};
|
||||
|
||||
panel.paint(&mut cx, rect);
|
||||
|
||||
assert!(
|
||||
backend
|
||||
.round_fills
|
||||
.iter()
|
||||
.any(|(r, color)| rect_eq(*r, target) && color_eq(*color, expected)),
|
||||
"pressed ACP connection button should paint the shared pressed feedback token"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ pub fn agent_settings_button(
|
|||
W::CancelBuiltinAgentDraft => Some(O::BuiltinCancelDraft),
|
||||
W::EditAcpAgent(index) => Some(O::AcpEdit(index)),
|
||||
W::RemoveAcpAgent(index) => Some(O::AcpRemove(index)),
|
||||
W::ToggleAcpConnected(index) => Some(O::AcpConnection(index)),
|
||||
W::SaveAcpAgentDraft => Some(O::AcpSaveDraft),
|
||||
W::CancelAcpAgentDraft => Some(O::AcpCancelDraft),
|
||||
W::ToggleMcpServer => Some(O::McpServer),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use super::WidgetHostNative;
|
||||
use op_editor_core::agent_settings::{AcpAgentField, SettingsFocus};
|
||||
use op_editor_core::{AgentSettingsButton, ButtonPressTarget};
|
||||
use op_editor_ui::widgets::agent_settings_panel::AgentSettingsPanel;
|
||||
|
||||
fn agent_settings_content_metrics(host: &WidgetHostNative) -> (f32, f32, f32) {
|
||||
|
|
@ -136,14 +137,30 @@ fn acp_agent_connect_press_toggles_connected_state() {
|
|||
let card_y = acp_card_y(content_y);
|
||||
let button_x = content_x + content_w - 60.0;
|
||||
assert!(host.dispatch_agent_settings_press(button_x, card_y + 30.0, 1200.0, 800.0));
|
||||
assert_eq!(
|
||||
host.editor_state().editor_ui.pressed_button,
|
||||
Some(ButtonPressTarget::AgentSettings(
|
||||
AgentSettingsButton::AcpConnection(0)
|
||||
))
|
||||
);
|
||||
assert!(
|
||||
host.editor_state().editor_ui.agent_settings.acp_agents[0].connected,
|
||||
"configured local ACP agent should become connected after pressing Connect"
|
||||
);
|
||||
assert!(host.apply_release_with_viewport(1200.0, 800.0));
|
||||
assert_eq!(host.editor_state().editor_ui.pressed_button, None);
|
||||
|
||||
assert!(host.dispatch_agent_settings_press(button_x, card_y + 30.0, 1200.0, 800.0));
|
||||
assert_eq!(
|
||||
host.editor_state().editor_ui.pressed_button,
|
||||
Some(ButtonPressTarget::AgentSettings(
|
||||
AgentSettingsButton::AcpConnection(0)
|
||||
))
|
||||
);
|
||||
assert!(
|
||||
!host.editor_state().editor_ui.agent_settings.acp_agents[0].connected,
|
||||
"connected ACP agent should become disconnected after pressing Disconnect"
|
||||
);
|
||||
assert!(host.apply_release_with_viewport(1200.0, 800.0));
|
||||
assert_eq!(host.editor_state().editor_ui.pressed_button, None);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ use op_editor_ui::{Point2D, Rect, Theme};
|
|||
|
||||
mod a11y_bridge;
|
||||
#[cfg(test)]
|
||||
mod agent_settings_acp_press_tests;
|
||||
#[cfg(test)]
|
||||
mod agent_settings_compact_press_tests;
|
||||
#[cfg(test)]
|
||||
mod agent_settings_form_press_tests;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
use super::WidgetHost;
|
||||
use op_editor_core::{AgentSettingsButton, ButtonPressTarget};
|
||||
use op_editor_ui::widgets::agent_settings_panel::AgentSettingsPanel;
|
||||
|
||||
fn content_metrics(host: &WidgetHost) -> (f32, f32, f32) {
|
||||
let panel = AgentSettingsPanel::for_editor(&host.editor_state);
|
||||
let rect = panel.rect(1200.0, 800.0);
|
||||
(
|
||||
rect.origin.x + 200.0 + 24.0,
|
||||
rect.origin.y + 24.0,
|
||||
rect.size.x - 200.0 - 48.0,
|
||||
)
|
||||
}
|
||||
|
||||
fn acp_card_y(content_y: f32) -> f32 {
|
||||
content_y + 12.0 + 120.0 + 28.0 + 28.0 + 28.0
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_agent_connect_press_sets_and_release_clears_agent_settings_button() {
|
||||
let mut host = WidgetHost::new();
|
||||
host.editor_state.editor_ui.agent_settings.add_acp_agent();
|
||||
host.editor_state.editor_ui.agent_settings.acp_agents[0].command = "op-agent".into();
|
||||
let (content_x, content_y, content_w) = content_metrics(&host);
|
||||
let button_x = content_x + content_w - 60.0;
|
||||
|
||||
assert!(host.dispatch_agent_settings_press(
|
||||
button_x,
|
||||
acp_card_y(content_y) + 30.0,
|
||||
1200.0,
|
||||
800.0,
|
||||
));
|
||||
assert_eq!(
|
||||
host.editor_state.editor_ui.pressed_button,
|
||||
Some(ButtonPressTarget::AgentSettings(
|
||||
AgentSettingsButton::AcpConnection(0)
|
||||
))
|
||||
);
|
||||
assert!(host.editor_state.editor_ui.agent_settings.acp_agents[0].connected);
|
||||
|
||||
assert!(host.apply_release_with_viewport(1200.0, 800.0));
|
||||
assert_eq!(host.editor_state.editor_ui.pressed_button, None);
|
||||
}
|
||||
Loading…
Reference in a new issue