From 3d1c0b6ae138d45bbaf66c59047e707b8e4c4285 Mon Sep 17 00:00:00 2001 From: Fini Date: Mon, 1 Jun 2026 06:10:37 +0800 Subject: [PATCH] fix(settings): open image provider menu --- crates/op-editor-core/src/agent_settings.rs | 3 + .../src/tests_agent_settings.rs | 1 + .../src/widgets/agent_settings_images.rs | 218 +++++------------- .../widgets/agent_settings_images_parts.rs | 206 ++++++++++++++++- .../src/widgets/agent_settings_panel.rs | 13 +- .../src/widgets/agent_settings_panel_tests.rs | 2 +- .../src/widget_host/agent_settings_tests.rs | 23 +- .../src/widget_host/press_helpers.rs | 28 ++- .../src/widget_host/agent_settings_press.rs | 28 ++- 9 files changed, 330 insertions(+), 192 deletions(-) diff --git a/crates/op-editor-core/src/agent_settings.rs b/crates/op-editor-core/src/agent_settings.rs index 1b7cbb260..f7ba94fd2 100644 --- a/crates/op-editor-core/src/agent_settings.rs +++ b/crates/op-editor-core/src/agent_settings.rs @@ -437,6 +437,7 @@ pub struct AgentSettings { pub openverse_client_secret: String, pub image_gen_profiles: Vec, pub active_image_gen_profile_id: Option, + pub image_gen_provider_menu_open: Option, pub next_image_gen_profile_id: u64, /// Whether the desktop host should check GitHub releases on /// startup. Manual "Check for Updates" stays available. @@ -477,6 +478,7 @@ impl Default for AgentSettings { openverse_client_secret: String::new(), image_gen_profiles: Vec::new(), active_image_gen_profile_id: None, + image_gen_provider_menu_open: None, next_image_gen_profile_id: 1, auto_update_enabled: true, focus: None, @@ -737,6 +739,7 @@ impl AgentSettings { if self.image_gen_profiles.len() == before { return false; } + self.image_gen_provider_menu_open = None; if self.active_image_gen_profile_id.as_deref() == Some(id) { self.active_image_gen_profile_id = self.image_gen_profiles.first().map(|p| p.id.clone()); diff --git a/crates/op-editor-core/src/tests_agent_settings.rs b/crates/op-editor-core/src/tests_agent_settings.rs index cac1d41c7..c0a1f0474 100644 --- a/crates/op-editor-core/src/tests_agent_settings.rs +++ b/crates/op-editor-core/src/tests_agent_settings.rs @@ -14,6 +14,7 @@ fn default_settings_are_quiescent() { assert!(s.acp_agent_draft.is_none()); assert!(s.image_gen_profiles.is_empty()); assert!(s.active_image_gen_profile_id.is_none()); + assert!(s.image_gen_provider_menu_open.is_none()); assert!(!s.images_advanced_open); assert!(s.openverse_client_id.is_empty()); assert!(s.openverse_client_secret.is_empty()); diff --git a/crates/op-editor-ui/src/widgets/agent_settings_images.rs b/crates/op-editor-ui/src/widgets/agent_settings_images.rs index 804f0580c..4cd9ff214 100644 --- a/crates/op-editor-ui/src/widgets/agent_settings_images.rs +++ b/crates/op-editor-ui/src/widgets/agent_settings_images.rs @@ -1,18 +1,17 @@ //! Images tab of the settings modal. use crate::theme::Theme; -use crate::widgets::agent_settings_caret::{ - caret_x_for_text, paint_caret, settings_caret_for_focus, -}; use crate::widgets::agent_settings_i18n::t as t_settings; use crate::widgets::agent_settings_images_parts::{ - ellipsize, paint_search_input_row, rect_contains, + ellipsize, paint_profile_field, paint_profile_test_button, paint_provider_field, + paint_provider_menu, paint_search_input_row, rect_contains, }; use crate::widgets::icons::{draw_icon, Icon}; use crate::widgets::PaintCx; use crate::{Color, Point2D, Rect, TextLayout}; use op_editor_core::agent_settings::{ - AgentSettings, ImageGenField, ImageGenProfile, ImageSearchField, SettingsFocus, + AgentSettings, ImageGenField, ImageGenProfile, ImageGenProvider, ImageSearchField, + SettingsFocus, }; use op_editor_core::editor_ui_state::EditorUiState; @@ -38,6 +37,7 @@ const PROFILE_FORM_TOP: f32 = 40.0; const PROFILE_FIELD_H: f32 = 24.0; const PROFILE_TEST_BTN_W: f32 = 56.0; const PROFILE_TEST_GAP: f32 = 8.0; +const PROVIDER_OPTION_H: f32 = 24.0; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ImagesHit { @@ -49,8 +49,15 @@ pub enum ImagesHit { SetActiveGenConfig(usize), RemoveGenConfig(usize), TestGenConfig(usize), - CycleGenProvider(usize), - FocusGenConfig { index: usize, field: ImageGenField }, + ToggleGenProviderMenu(usize), + SelectGenProvider { + index: usize, + provider: ImageGenProvider, + }, + FocusGenConfig { + index: usize, + field: ImageGenField, + }, None, } @@ -235,6 +242,17 @@ fn profile_provider_rect(row: Rect) -> Rect { profile_field_rect(row, 1) } +fn profile_provider_option_rect(row: Rect, option_index: usize) -> Rect { + let provider = profile_provider_rect(row); + Rect { + origin: Point2D::new( + provider.origin.x, + provider.origin.y + provider.size.y + option_index as f32 * PROVIDER_OPTION_H, + ), + size: Point2D::new(provider.size.x, PROVIDER_OPTION_H), + } +} + fn profile_field_index(field: ImageGenField) -> usize { match field { ImageGenField::Name => 0, @@ -266,6 +284,16 @@ pub fn hit_test(content: Rect, settings: &AgentSettings, scrolled: Point2D) -> I } for (index, profile) in settings.image_gen_profiles.iter().enumerate() { let row = profile_row_rect(content, settings, index); + if settings.image_gen_provider_menu_open == Some(index) { + for (option_index, provider) in ImageGenProvider::ALL.iter().enumerate() { + if rect_contains(profile_provider_option_rect(row, option_index), scrolled) { + return ImagesHit::SelectGenProvider { + index, + provider: *provider, + }; + } + } + } if rect_contains(profile_active_rect(row), scrolled) { return ImagesHit::SetActiveGenConfig(index); } @@ -283,7 +311,7 @@ pub fn hit_test(content: Rect, settings: &AgentSettings, scrolled: Point2D) -> I return ImagesHit::None; } if rect_contains(profile_provider_rect(row), scrolled) { - return ImagesHit::CycleGenProvider(index); + return ImagesHit::ToggleGenProviderMenu(index); } for field in image_gen_fields() { if rect_contains(profile_input_rect(row, field), scrolled) { @@ -628,168 +656,28 @@ fn paint_profile_row( if editing { for field in image_gen_fields() { - paint_profile_field(cx, theme, settings, ui, profile, index, field, row, now_ms); + paint_profile_field( + cx, + theme, + settings, + ui, + profile, + index, + field, + profile_input_rect(row, field), + row.origin.x + 12.0, + now_ms, + ); } - paint_profile_test_button(cx, theme, ui, profile, row); - paint_provider_field(cx, theme, profile, row); - } -} - -#[allow(clippy::too_many_arguments)] -fn paint_profile_field( - cx: &mut PaintCx<'_>, - theme: &Theme, - settings: &AgentSettings, - ui: &EditorUiState, - profile: &ImageGenProfile, - index: usize, - field: ImageGenField, - row: Rect, - now_ms: u64, -) { - let focus = SettingsFocus::ImageGenProfile { index, field }; - let focused = settings.focus == Some(focus); - let value = if focused { - ui.settings_input_draft.as_str() - } else { - match field { - ImageGenField::Name => profile.name.as_str(), - ImageGenField::ApiKey if !profile.api_key.is_empty() => "********", - ImageGenField::ApiKey => "", - ImageGenField::Model => profile.model.as_str(), - ImageGenField::BaseUrl => profile.base_url.as_deref().unwrap_or(""), + paint_profile_test_button(cx, theme, ui, profile, profile_test_btn_rect(row)); + let provider_rect = profile_provider_rect(row); + paint_provider_field(cx, theme, profile, provider_rect, row.origin.x + 12.0); + if settings.image_gen_provider_menu_open == Some(index) { + paint_provider_menu(cx, theme, provider_rect, profile.provider); } - }; - let label = match field { - ImageGenField::Name => "Name", - ImageGenField::ApiKey => "API Key", - ImageGenField::Model => "Model", - ImageGenField::BaseUrl => "Base URL", - }; - let input = profile_input_rect(row, field); - let label_lay = TextLayout::single_run( - label, - "system-ui", - 11.0, - to_jian(theme.muted_foreground), - Point2D::new(0.0, 0.0), - ); - cx.backend.draw_text( - &label_lay, - Point2D::new(row.origin.x + 12.0, input.origin.y + 16.0), - ); - cx.backend.fill_round_rect( - input, - 6.0, - if focused { - theme.background - } else { - theme.card - }, - ); - cx.backend.stroke_round_rect( - input, - 6.0, - if focused { theme.primary } else { theme.border }, - 1.0, - ); - let clipped = ellipsize(cx, value, input.size.x - 12.0, 11.0); - let text_x = input.origin.x + 6.0; - let value_lay = TextLayout::single_run( - &clipped, - "system-ui", - 11.0, - to_jian(theme.foreground), - Point2D::new(0.0, 0.0), - ); - cx.backend - .draw_text(&value_lay, Point2D::new(text_x, input.origin.y + 16.0)); - if focused { - let caret = settings_caret_for_focus(ui, focus); - let caret_x = caret_x_for_text( - cx, - value, - caret, - text_x, - input.origin.x + input.size.x - 8.0, - 11.0, - ); - let caret_y = input.origin.y + 4.5; - let anchor = ui.settings_input_caret_anchor_ms; - paint_caret(cx, theme, now_ms, anchor, caret_x, caret_y); } } -fn paint_profile_test_button( - cx: &mut PaintCx<'_>, - theme: &Theme, - ui: &EditorUiState, - profile: &ImageGenProfile, - row: Rect, -) { - let btn = profile_test_btn_rect(row); - let enabled = !profile.api_key.trim().is_empty(); - cx.backend.fill_round_rect(btn, 6.0, theme.muted); - cx.backend.stroke_round_rect(btn, 6.0, theme.border, 1.0); - let label = t_settings(ui, "settings.images.test"); - let label_w = cx.backend.measure_text(label, 11.0); - let layout = TextLayout::single_run( - label, - "system-ui", - 11.0, - to_jian(if enabled { - theme.foreground - } else { - theme.muted_foreground - }), - Point2D::new(0.0, 0.0), - ); - cx.backend.draw_text( - &layout, - Point2D::new( - btn.origin.x + (btn.size.x - label_w) / 2.0, - btn.origin.y + btn.size.y / 2.0 + 4.0, - ), - ); -} - -fn paint_provider_field(cx: &mut PaintCx<'_>, theme: &Theme, profile: &ImageGenProfile, row: Rect) { - let input = profile_provider_rect(row); - let label_lay = TextLayout::single_run( - "Provider", - "system-ui", - 11.0, - to_jian(theme.muted_foreground), - Point2D::new(0.0, 0.0), - ); - cx.backend.draw_text( - &label_lay, - Point2D::new(row.origin.x + 12.0, input.origin.y + 16.0), - ); - cx.backend.fill_round_rect(input, 6.0, theme.card); - cx.backend.stroke_round_rect(input, 6.0, theme.border, 1.0); - let value = ellipsize(cx, profile.provider.label(), input.size.x - 28.0, 11.0); - let value_lay = TextLayout::single_run( - &value, - "system-ui", - 11.0, - to_jian(theme.foreground), - Point2D::new(0.0, 0.0), - ); - cx.backend.draw_text( - &value_lay, - Point2D::new(input.origin.x + 6.0, input.origin.y + 16.0), - ); - draw_icon( - cx.backend, - Icon::ChevronDown, - Point2D::new(input.origin.x + input.size.x - 18.0, input.origin.y + 5.0), - 14.0, - theme.muted_foreground, - 1.5, - ); -} - fn image_gen_fields() -> [ImageGenField; 4] { use ImageGenField::*; [Name, ApiKey, Model, BaseUrl] diff --git a/crates/op-editor-ui/src/widgets/agent_settings_images_parts.rs b/crates/op-editor-ui/src/widgets/agent_settings_images_parts.rs index 9acf5faac..52ed799e6 100644 --- a/crates/op-editor-ui/src/widgets/agent_settings_images_parts.rs +++ b/crates/op-editor-ui/src/widgets/agent_settings_images_parts.rs @@ -4,13 +4,18 @@ use crate::theme::Theme; use crate::widgets::agent_settings_caret::{ caret_x_for_text, paint_caret, settings_caret_for_focus, }; +use crate::widgets::icons::{draw_icon, Icon}; use crate::widgets::PaintCx; use crate::{Color, Point2D, Rect, TextLayout}; -use op_editor_core::agent_settings::{AgentSettings, ImageSearchField, SettingsFocus}; +use op_editor_core::agent_settings::{ + AgentSettings, ImageGenField, ImageGenProfile, ImageGenProvider, ImageSearchField, + SettingsFocus, +}; use op_editor_core::editor_ui_state::EditorUiState; const ROW_H: f32 = 36.0; const LABEL_W: f32 = 110.0; +const PROVIDER_OPTION_H: f32 = 24.0; #[allow(clippy::too_many_arguments)] pub(super) fn paint_search_input_row( @@ -98,6 +103,205 @@ pub(super) fn paint_search_input_row( } } +#[allow(clippy::too_many_arguments)] +pub(super) fn paint_profile_field( + cx: &mut PaintCx<'_>, + theme: &Theme, + settings: &AgentSettings, + ui: &EditorUiState, + profile: &ImageGenProfile, + index: usize, + field: ImageGenField, + input: Rect, + label_x: f32, + now_ms: u64, +) { + let focus = SettingsFocus::ImageGenProfile { index, field }; + let focused = settings.focus == Some(focus); + let value = if focused { + ui.settings_input_draft.as_str() + } else { + match field { + ImageGenField::Name => profile.name.as_str(), + ImageGenField::ApiKey if !profile.api_key.is_empty() => "********", + ImageGenField::ApiKey => "", + ImageGenField::Model => profile.model.as_str(), + ImageGenField::BaseUrl => profile.base_url.as_deref().unwrap_or(""), + } + }; + let label = match field { + ImageGenField::Name => "Name", + ImageGenField::ApiKey => "API Key", + ImageGenField::Model => "Model", + ImageGenField::BaseUrl => "Base URL", + }; + let label_lay = TextLayout::single_run( + label, + "system-ui", + 11.0, + to_jian(theme.muted_foreground), + Point2D::new(0.0, 0.0), + ); + cx.backend + .draw_text(&label_lay, Point2D::new(label_x, input.origin.y + 16.0)); + cx.backend.fill_round_rect( + input, + 6.0, + if focused { + theme.background + } else { + theme.card + }, + ); + cx.backend.stroke_round_rect( + input, + 6.0, + if focused { theme.primary } else { theme.border }, + 1.0, + ); + let clipped = ellipsize(cx, value, input.size.x - 12.0, 11.0); + let text_x = input.origin.x + 6.0; + let value_lay = TextLayout::single_run( + &clipped, + "system-ui", + 11.0, + to_jian(theme.foreground), + Point2D::new(0.0, 0.0), + ); + cx.backend + .draw_text(&value_lay, Point2D::new(text_x, input.origin.y + 16.0)); + if focused { + let caret = settings_caret_for_focus(ui, focus); + let caret_x = caret_x_for_text( + cx, + value, + caret, + text_x, + input.origin.x + input.size.x - 8.0, + 11.0, + ); + let caret_y = input.origin.y + 4.5; + let anchor = ui.settings_input_caret_anchor_ms; + paint_caret(cx, theme, now_ms, anchor, caret_x, caret_y); + } +} + +pub(super) fn paint_profile_test_button( + cx: &mut PaintCx<'_>, + theme: &Theme, + ui: &EditorUiState, + profile: &ImageGenProfile, + btn: Rect, +) { + let enabled = !profile.api_key.trim().is_empty(); + cx.backend.fill_round_rect(btn, 6.0, theme.muted); + cx.backend.stroke_round_rect(btn, 6.0, theme.border, 1.0); + let label = crate::widgets::agent_settings_i18n::t(ui, "settings.images.test"); + let label_w = cx.backend.measure_text(label, 11.0); + let layout = TextLayout::single_run( + label, + "system-ui", + 11.0, + to_jian(if enabled { + theme.foreground + } else { + theme.muted_foreground + }), + Point2D::new(0.0, 0.0), + ); + cx.backend.draw_text( + &layout, + Point2D::new( + btn.origin.x + (btn.size.x - label_w) / 2.0, + btn.origin.y + btn.size.y / 2.0 + 4.0, + ), + ); +} + +pub(super) fn paint_provider_field( + cx: &mut PaintCx<'_>, + theme: &Theme, + profile: &ImageGenProfile, + input: Rect, + label_x: f32, +) { + let label_lay = TextLayout::single_run( + "Provider", + "system-ui", + 11.0, + to_jian(theme.muted_foreground), + Point2D::new(0.0, 0.0), + ); + cx.backend + .draw_text(&label_lay, Point2D::new(label_x, input.origin.y + 16.0)); + cx.backend.fill_round_rect(input, 6.0, theme.card); + cx.backend.stroke_round_rect(input, 6.0, theme.border, 1.0); + let value = ellipsize(cx, profile.provider.label(), input.size.x - 28.0, 11.0); + let value_lay = TextLayout::single_run( + &value, + "system-ui", + 11.0, + to_jian(theme.foreground), + Point2D::new(0.0, 0.0), + ); + cx.backend.draw_text( + &value_lay, + Point2D::new(input.origin.x + 6.0, input.origin.y + 16.0), + ); + draw_icon( + cx.backend, + Icon::ChevronDown, + Point2D::new(input.origin.x + input.size.x - 18.0, input.origin.y + 5.0), + 14.0, + theme.muted_foreground, + 1.5, + ); +} + +pub(super) fn paint_provider_menu( + cx: &mut PaintCx<'_>, + theme: &Theme, + input: Rect, + selected: ImageGenProvider, +) { + let menu = Rect { + origin: Point2D::new(input.origin.x, input.origin.y + input.size.y), + size: Point2D::new( + input.size.x, + PROVIDER_OPTION_H * ImageGenProvider::ALL.len() as f32, + ), + }; + cx.backend.fill_round_rect(menu, 6.0, theme.card); + cx.backend.stroke_round_rect(menu, 6.0, theme.border, 1.0); + for (index, provider) in ImageGenProvider::ALL.iter().enumerate() { + let option_y = menu.origin.y + index as f32 * PROVIDER_OPTION_H; + if *provider == selected { + cx.backend.fill_round_rect( + Rect { + origin: Point2D::new(menu.origin.x + 2.0, option_y + 2.0), + size: Point2D::new(menu.size.x - 4.0, PROVIDER_OPTION_H - 4.0), + }, + 4.0, + theme.muted, + ); + } + let label = TextLayout::single_run( + provider.label(), + "system-ui", + 11.0, + to_jian(theme.foreground), + Point2D::new(0.0, 0.0), + ); + cx.backend.draw_text( + &label, + Point2D::new( + menu.origin.x + 8.0, + option_y + PROVIDER_OPTION_H / 2.0 + 4.0, + ), + ); + } +} + pub(super) fn ellipsize(cx: &mut PaintCx<'_>, value: &str, max_w: f32, size: f32) -> String { if cx.backend.measure_text(value, size) <= max_w { return value.to_string(); diff --git a/crates/op-editor-ui/src/widgets/agent_settings_panel.rs b/crates/op-editor-ui/src/widgets/agent_settings_panel.rs index d860b7396..7020f1127 100644 --- a/crates/op-editor-ui/src/widgets/agent_settings_panel.rs +++ b/crates/op-editor-ui/src/widgets/agent_settings_panel.rs @@ -88,7 +88,11 @@ pub enum AgentSettingsHit { SetActiveGenConfig(usize), RemoveGenConfig(usize), TestGenConfig(usize), - CycleGenProvider(usize), + ToggleGenProviderMenu(usize), + SelectGenProvider { + index: usize, + provider: op_editor_core::agent_settings::ImageGenProvider, + }, FocusGenConfig { index: usize, field: ImageGenField, @@ -257,8 +261,11 @@ impl<'a> AgentSettingsPanel<'a> { ImagesHit::TestGenConfig(index) => { return AgentSettingsHit::TestGenConfig(index); } - ImagesHit::CycleGenProvider(index) => { - return AgentSettingsHit::CycleGenProvider(index); + ImagesHit::ToggleGenProviderMenu(index) => { + return AgentSettingsHit::ToggleGenProviderMenu(index); + } + ImagesHit::SelectGenProvider { index, provider } => { + return AgentSettingsHit::SelectGenProvider { index, provider }; } ImagesHit::FocusGenConfig { index, field } => { return AgentSettingsHit::FocusGenConfig { index, field }; diff --git a/crates/op-editor-ui/src/widgets/agent_settings_panel_tests.rs b/crates/op-editor-ui/src/widgets/agent_settings_panel_tests.rs index a66d977a6..c6bfae0dd 100644 --- a/crates/op-editor-ui/src/widgets/agent_settings_panel_tests.rs +++ b/crates/op-editor-ui/src/widgets/agent_settings_panel_tests.rs @@ -891,6 +891,6 @@ fn images_tab_expanded_profile_provider_row_is_clickable() { rect, crate::Point2D::new(content_x + 110.0 + 20.0, provider_y + 12.0) ), - AgentSettingsHit::CycleGenProvider(0) + AgentSettingsHit::ToggleGenProviderMenu(0) ); } diff --git a/crates/op-host-native/src/widget_host/agent_settings_tests.rs b/crates/op-host-native/src/widget_host/agent_settings_tests.rs index 457f85387..3a7dfb499 100644 --- a/crates/op-host-native/src/widget_host/agent_settings_tests.rs +++ b/crates/op-host-native/src/widget_host/agent_settings_tests.rs @@ -612,7 +612,7 @@ fn image_generation_profile_focus_accepts_text_and_commits() { } #[test] -fn image_generation_provider_click_cycles_provider_and_clears_model() { +fn image_generation_provider_click_opens_menu_without_changing_profile() { let mut host = WidgetHostNative::new(); host.editor_state_mut().editor_ui.agent_settings.tab = AgentSettingsTab::Images; host.editor_state_mut() @@ -649,9 +649,28 @@ fn image_generation_provider_click_cycles_provider_and_clears_model() { .editor_ui .agent_settings .image_gen_profiles[0]; + assert_eq!(profile.provider, ImageGenProvider::OpenAi); + assert_eq!(profile.model, "dall-e-3"); + assert!(host.editor_state().editor_ui.agent_settings.focus.is_none()); + assert_eq!( + host.editor_state() + .editor_ui + .agent_settings + .image_gen_provider_menu_open, + Some(0) + ); + + assert!(host.dispatch_agent_settings_press( + content_x + 110.0 + 20.0, + provider_y + 60.0, + 1200.0, + 800.0 + )); + let settings = &host.editor_state().editor_ui.agent_settings; + let profile = &settings.image_gen_profiles[0]; assert_eq!(profile.provider, ImageGenProvider::Gemini); assert!(profile.model.is_empty()); - assert!(host.editor_state().editor_ui.agent_settings.focus.is_none()); + assert!(settings.image_gen_provider_menu_open.is_none()); } #[test] diff --git a/crates/op-host-native/src/widget_host/press_helpers.rs b/crates/op-host-native/src/widget_host/press_helpers.rs index db7dbf81e..77d043f0b 100644 --- a/crates/op-host-native/src/widget_host/press_helpers.rs +++ b/crates/op-host-native/src/widget_host/press_helpers.rs @@ -197,21 +197,29 @@ impl WidgetHostNative { ); } } - AgentSettingsHit::CycleGenProvider(index) => { + AgentSettingsHit::ToggleGenProviderMenu(index) => { self.commit_settings_focus_if_any(); - if let Some(profile) = self - .editor_state - .editor_ui - .agent_settings - .image_gen_profiles - .get_mut(index) - { - profile.provider = profile.provider.next(); - profile.model.clear(); + let settings = &mut self.editor_state.editor_ui.agent_settings; + settings.image_gen_provider_menu_open = + (settings.image_gen_provider_menu_open != Some(index)).then_some(index); + } + AgentSettingsHit::SelectGenProvider { index, provider } => { + self.commit_settings_focus_if_any(); + let settings = &mut self.editor_state.editor_ui.agent_settings; + if let Some(profile) = settings.image_gen_profiles.get_mut(index) { + if profile.provider != provider { + profile.provider = provider; + profile.model.clear(); + } } + settings.image_gen_provider_menu_open = None; } AgentSettingsHit::FocusGenConfig { index, field } => { self.commit_settings_focus_if_any(); + self.editor_state + .editor_ui + .agent_settings + .image_gen_provider_menu_open = None; self.focus_image_gen_profile(index, field); } AgentSettingsHit::ToggleAutoUpdate => { diff --git a/crates/op-host-web/src/widget_host/agent_settings_press.rs b/crates/op-host-web/src/widget_host/agent_settings_press.rs index 7f618bcbb..4612e2e21 100644 --- a/crates/op-host-web/src/widget_host/agent_settings_press.rs +++ b/crates/op-host-web/src/widget_host/agent_settings_press.rs @@ -181,21 +181,29 @@ impl WidgetHost { self.editor_state.editor_ui.settings_input_caret_anchor_ms = self.now_ms; } } - AgentSettingsHit::CycleGenProvider(index) => { + AgentSettingsHit::ToggleGenProviderMenu(index) => { self.commit_settings_focus(); - if let Some(profile) = self - .editor_state - .editor_ui - .agent_settings - .image_gen_profiles - .get_mut(index) - { - profile.provider = profile.provider.next(); - profile.model.clear(); + let settings = &mut self.editor_state.editor_ui.agent_settings; + settings.image_gen_provider_menu_open = + (settings.image_gen_provider_menu_open != Some(index)).then_some(index); + } + AgentSettingsHit::SelectGenProvider { index, provider } => { + self.commit_settings_focus(); + let settings = &mut self.editor_state.editor_ui.agent_settings; + if let Some(profile) = settings.image_gen_profiles.get_mut(index) { + if profile.provider != provider { + profile.provider = provider; + profile.model.clear(); + } } + settings.image_gen_provider_menu_open = None; } AgentSettingsHit::FocusGenConfig { index, field } => { self.commit_settings_focus(); + self.editor_state + .editor_ui + .agent_settings + .image_gen_provider_menu_open = None; self.focus_image_gen_profile(index, field); } AgentSettingsHit::ToggleAutoUpdate => {