From 1edb1b389613abbd2b62b07aff0be7350e078829 Mon Sep 17 00:00:00 2001 From: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com> Date: Thu, 23 Oct 2025 00:49:36 -0400 Subject: [PATCH] settings ui: Update file headers when adding or removing projects (#40968) This PR gets the `SettingsWindow` struct to subscribe to all `Entity` events and any future project entities that are created. When a project emits an event that signals a worktree has been added or removed, the settings window refetches all settings files it can find. This fixes a bug where the settings ui would notice some project settings that were created or opened after the `SettingsWindow` has been initialized. I also renamed `LOCAL` file mask to `PROJECT` to be inline with the `SettingsFile` naming convention. Release Notes: - settings ui: Fix bug where project setting files wouldn't be detected if they were created or opened after while an active settings window is open --- crates/settings_ui/src/page_data.rs | 146 ++++++++++---------- crates/settings_ui/src/settings_ui.rs | 187 +++++++++++++++----------- crates/workspace/src/workspace.rs | 2 +- 3 files changed, 186 insertions(+), 149 deletions(-) diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 5997d14de33bdec4b4739112a02b22ac0f12e2e7..5c75a78b9a6dae79abbc7d96089512d1a5063949 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -5,7 +5,7 @@ use strum::IntoDiscriminant as _; use ui::{IntoElement, SharedString}; use crate::{ - DynamicItem, LOCAL, SettingField, SettingItem, SettingsFieldMetadata, SettingsPage, + DynamicItem, PROJECT, SettingField, SettingItem, SettingsFieldMetadata, SettingsPage, SettingsPageItem, SubPageLink, USER, all_language_names, sub_page_stack, }; @@ -26,7 +26,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { items: vec![ SettingsPageItem::SectionHeader("General Settings"), SettingsPageItem::SettingItem(SettingItem { - files: LOCAL, + files: PROJECT, title: "Project Name", description: "The displayed name of this project. If left empty, the root directory name will be displayed.", field: Box::new( @@ -1022,7 +1022,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), // todo(settings_ui): This needs a custom component SettingsPageItem::SettingItem(SettingItem { @@ -1046,7 +1046,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), ], }, @@ -2117,7 +2117,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), ]); @@ -2314,7 +2314,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { items.extend(all_language_names(cx).into_iter().map(|language_name| { SettingsPageItem::SubPageLink(SubPageLink { title: language_name, - files: USER | LOCAL, + files: USER | PROJECT, render: Arc::new(|this, window, cx| { this.render_sub_page_items( language_settings_data() @@ -4432,7 +4432,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { SettingsPageItem::SectionHeader("Environment"), SettingsPageItem::DynamicItem(DynamicItem { discriminant: SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Shell", description: "What shell to use when opening a terminal.", field: Box::new(SettingField { @@ -4496,7 +4496,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { settings::ShellDiscriminants::System => vec![], settings::ShellDiscriminants::Program => vec![ SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Program", description: "The shell program to use.", field: Box::new(SettingField { @@ -4526,7 +4526,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { ], settings::ShellDiscriminants::WithArguments => vec![ SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Program", description: "The shell program to run.", field: Box::new(SettingField { @@ -4554,7 +4554,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, }, SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Arguments", description: "The arguments to pass to the shell program.", field: Box::new( @@ -4585,7 +4585,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, }, SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Title Override", description: "An optional string to override the title of the terminal tab.", field: Box::new(SettingField { @@ -4615,7 +4615,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { }), SettingsPageItem::DynamicItem(DynamicItem { discriminant: SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Working Directory", description: "What working directory to use when launching the terminal.", field: Box::new(SettingField { @@ -4672,7 +4672,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![], settings::WorkingDirectoryDiscriminants::Always => vec![ SettingItem { - files: USER | LOCAL, + files: USER | PROJECT, title: "Directory", description: "The directory path to use (will be shell expanded).", field: Box::new(SettingField { @@ -4721,7 +4721,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Detect Virtual Environment", @@ -4748,7 +4748,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Font"), SettingsPageItem::SettingItem(SettingItem { @@ -5813,7 +5813,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Hard Tabs", @@ -5832,7 +5832,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Auto Indent", @@ -5851,7 +5851,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Auto Indent On Paste", @@ -5870,7 +5870,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Wrapping"), SettingsPageItem::SettingItem(SettingItem { @@ -5890,7 +5890,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Wrap Guides", @@ -5909,7 +5909,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Preferred Line Length", @@ -5928,7 +5928,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Wrap Guides", @@ -5950,7 +5950,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Allow Rewrap", @@ -5969,7 +5969,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Indent Guides"), SettingsPageItem::SettingItem(SettingItem { @@ -5992,7 +5992,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Line Width", @@ -6014,7 +6014,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Active Line Width", @@ -6039,7 +6039,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Coloring", @@ -6061,7 +6061,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Background Coloring", @@ -6086,7 +6086,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Formatting"), SettingsPageItem::SettingItem(SettingItem { @@ -6109,7 +6109,7 @@ fn language_settings_data() -> Vec { }, ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Remove Trailing Whitespace On Save", @@ -6128,7 +6128,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Ensure Final Newline On Save", @@ -6147,7 +6147,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Formatter", @@ -6169,7 +6169,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Use On Type Format", @@ -6188,7 +6188,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Code Actions On Format", @@ -6210,7 +6210,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Autoclose"), SettingsPageItem::SettingItem(SettingItem { @@ -6230,7 +6230,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Use Auto Surround", @@ -6249,7 +6249,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Always Treat Brackets As Autoclosed", @@ -6268,7 +6268,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Jsx Tag Auto Close", @@ -6288,7 +6288,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Edit Predictions"), SettingsPageItem::SettingItem(SettingItem { @@ -6308,7 +6308,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Edit Predictions Disabled In", @@ -6330,7 +6330,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Whitespace"), SettingsPageItem::SettingItem(SettingItem { @@ -6350,7 +6350,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Space Whitespace Indicator", @@ -6372,7 +6372,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Tab Whitespace Indicator", @@ -6394,7 +6394,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Completions"), SettingsPageItem::SettingItem(SettingItem { @@ -6414,7 +6414,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Completion Documentation", @@ -6433,7 +6433,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Words", @@ -6452,7 +6452,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Words Min Length", @@ -6474,7 +6474,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Inlay Hints"), SettingsPageItem::SettingItem(SettingItem { @@ -6494,7 +6494,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Value Hints", @@ -6516,7 +6516,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Type Hints", @@ -6535,7 +6535,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Parameter Hints", @@ -6557,7 +6557,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Other Hints", @@ -6579,7 +6579,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Show Background", @@ -6598,7 +6598,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Edit Debounce Ms", @@ -6620,7 +6620,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Scroll Debounce Ms", @@ -6642,7 +6642,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Toggle On Modifiers Press", @@ -6671,7 +6671,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), ]; if current_language().is_none() { @@ -6709,7 +6709,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Variables", @@ -6732,7 +6732,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Prefer LSP", @@ -6752,7 +6752,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Miscellaneous"), SettingsPageItem::SettingItem(SettingItem { @@ -6774,7 +6774,7 @@ fn language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Middle Click Paste", @@ -6805,7 +6805,7 @@ fn language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), ]); @@ -6886,7 +6886,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Language Servers", @@ -6908,7 +6908,7 @@ fn non_editor_language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Linked Edits", @@ -6927,7 +6927,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Go To Definition Fallback", @@ -6960,7 +6960,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Fetch Timeout (milliseconds)", @@ -6982,7 +6982,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Insert Mode", @@ -7001,7 +7001,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Debuggers"), SettingsPageItem::SettingItem(SettingItem { @@ -7024,7 +7024,7 @@ fn non_editor_language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SectionHeader("Prettier"), SettingsPageItem::SettingItem(SettingItem { @@ -7044,7 +7044,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Parser", @@ -7063,7 +7063,7 @@ fn non_editor_language_settings_data() -> Vec { }, }), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Plugins", @@ -7085,7 +7085,7 @@ fn non_editor_language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), SettingsPageItem::SettingItem(SettingItem { title: "Options", @@ -7107,7 +7107,7 @@ fn non_editor_language_settings_data() -> Vec { .unimplemented(), ), metadata: None, - files: USER | LOCAL, + files: USER | PROJECT, }), ] } diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index de987da4b00fa5bc2a164f213a73e03523a46947..804cf1ad8d12771266b1104dff7b3fc1b1cf97d4 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -12,7 +12,7 @@ use gpui::{ point, prelude::*, px, uniform_list, }; use heck::ToTitleCase as _; -use project::WorktreeId; +use project::{Project, WorktreeId}; use schemars::JsonSchema; use serde::Deserialize; use settings::{Settings, SettingsContent, SettingsStore}; @@ -33,7 +33,7 @@ use ui::{ }; use ui_input::{NumberField, NumberFieldType}; use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath}; -use workspace::{OpenOptions, OpenVisible, Workspace, client_side_decorations}; +use workspace::{AppState, OpenOptions, OpenVisible, Workspace, client_side_decorations}; use zed_actions::{OpenSettings, OpenSettingsAt}; use crate::components::{SettingsInputField, font_picker, icon_theme_picker, theme_picker}; @@ -559,7 +559,6 @@ pub fn open_settings_editor( existing_window .update(cx, |settings_window, window, cx| { settings_window.original_window = Some(workspace_handle); - settings_window.observe_last_window_close(cx); window.activate_window(); if let Some(path) = path { open_path(path, settings_window, window, cx); @@ -643,7 +642,6 @@ pub struct SettingsWindow { title_bar: Option>, original_window: Option>, files: Vec<(SettingsUiFile, FocusHandle)>, - drop_down_file: Option, worktree_root_dirs: HashMap, current_file: SettingsUiFile, pages: Vec, @@ -1016,7 +1014,7 @@ impl std::fmt::Debug for FileMask { if self.contains(USER) { items.push("USER"); } - if self.contains(LOCAL) { + if self.contains(PROJECT) { items.push("LOCAL"); } if self.contains(SERVER) { @@ -1028,7 +1026,7 @@ impl std::fmt::Debug for FileMask { } const USER: FileMask = FileMask(1 << 0); -const LOCAL: FileMask = FileMask(1 << 2); +const PROJECT: FileMask = FileMask(1 << 2); const SERVER: FileMask = FileMask(1 << 3); impl std::ops::BitAnd for FileMask { @@ -1138,14 +1136,14 @@ impl SettingsUiFile { fn mask(&self) -> FileMask { match self { SettingsUiFile::User => USER, - SettingsUiFile::Project(_) => LOCAL, + SettingsUiFile::Project(_) => PROJECT, SettingsUiFile::Server(_) => SERVER, } } } impl SettingsWindow { - pub fn new( + fn new( original_window: Option>, window: &mut Window, cx: &mut Context, @@ -1182,6 +1180,60 @@ impl SettingsWindow { }) .detach(); + cx.on_window_closed(|cx| { + if let Some(existing_window) = cx + .windows() + .into_iter() + .find_map(|window| window.downcast::()) + && cx.windows().len() == 1 + { + cx.update_window(*existing_window, |_, window, _| { + window.remove_window(); + }) + .ok(); + } + }) + .detach(); + + if let Some(app_state) = AppState::global(cx).upgrade() { + for project in app_state + .workspace_store + .read(cx) + .workspaces() + .iter() + .filter_map(|space| { + space + .read(cx) + .ok() + .map(|workspace| workspace.project().clone()) + }) + .collect::>() + { + cx.subscribe_in(&project, window, Self::handle_project_event) + .detach(); + } + } else { + log::error!("App state doesn't exist when creating a new settings window"); + } + + let this_weak = cx.weak_entity(); + cx.observe_new::({ + move |_, window, cx| { + let project = cx.entity(); + let Some(window) = window else { + return; + }; + + this_weak + .update(cx, |_, cx| { + cx.subscribe_in(&project, window, Self::handle_project_event) + .detach(); + }) + .ok(); + } + }) + .detach(); + let title_bar = if !cfg!(target_os = "macos") { Some(cx.new(|cx| PlatformTitleBar::new("settings-title-bar", cx))) } else { @@ -1198,7 +1250,7 @@ impl SettingsWindow { worktree_root_dirs: HashMap::default(), files: vec![], - drop_down_file: None, + current_file: current_file, pages: vec![], navbar_entries: vec![], @@ -1232,8 +1284,6 @@ impl SettingsWindow { list_state, }; - this.observe_last_window_close(cx); - this.fetch_files(window, cx); this.build_ui(window, cx); this.build_search_index(); @@ -1245,21 +1295,21 @@ impl SettingsWindow { this } - fn observe_last_window_close(&mut self, cx: &mut App) { - cx.on_window_closed(|cx| { - if let Some(existing_window) = cx - .windows() - .into_iter() - .find_map(|window| window.downcast::()) - && cx.windows().len() == 1 - { - cx.update_window(*existing_window, |_, window, _| { - window.remove_window(); - }) - .ok(); + fn handle_project_event( + &mut self, + _: &Entity, + event: &project::Event, + window: &mut Window, + cx: &mut Context, + ) { + match event { + project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded(_) => { + cx.defer_in(window, |this, window, cx| { + this.fetch_files(window, cx); + }); } - }) - .detach(); + _ => {} + } } fn toggle_navbar_entry(&mut self, nav_entry_index: usize) { @@ -1702,7 +1752,7 @@ impl SettingsWindow { .iter() .any(|(file, _)| file == &self.current_file); if !current_file_still_exists { - self.change_file(0, false, window, cx); + self.change_file(0, window, cx); } } @@ -1732,21 +1782,12 @@ impl SettingsWindow { self.open_navbar_entry_page(first_navbar_entry_index); } - fn change_file( - &mut self, - ix: usize, - drop_down_file: bool, - window: &mut Window, - cx: &mut Context, - ) { + fn change_file(&mut self, ix: usize, window: &mut Window, cx: &mut Context) { if ix >= self.files.len() { self.current_file = SettingsUiFile::User; self.build_ui(window, cx); return; } - if drop_down_file { - self.drop_down_file = Some(ix); - } if self.files[ix].0 == self.current_file { return; @@ -1770,7 +1811,7 @@ impl SettingsWindow { window: &mut Window, cx: &mut Context, ) -> impl IntoElement { - const OVERFLOW_LIMIT: usize = 1; + static OVERFLOW_LIMIT: usize = 1; let file_button = |ix, file: &SettingsUiFile, focus_handle, cx: &mut Context| { @@ -1785,7 +1826,7 @@ impl SettingsWindow { .on_click(cx.listener({ let focus_handle = focus_handle.clone(); move |this, _: &gpui::ClickEvent, window, cx| { - this.change_file(ix, false, window, cx); + this.change_file(ix, window, cx); focus_handle.focus(window); } })) @@ -1810,25 +1851,24 @@ impl SettingsWindow { ), ) .when(self.files.len() > OVERFLOW_LIMIT, |div| { - div.children( - self.files - .iter() - .enumerate() - .skip(OVERFLOW_LIMIT) - .find(|(_, (file, _))| file == &self.current_file) - .map(|(ix, (file, focus_handle))| { - file_button(ix, file, focus_handle, cx) - }) - .or_else(|| { - let ix = self.drop_down_file.unwrap_or(OVERFLOW_LIMIT); - self.files.get(ix).map(|(file, focus_handle)| { - file_button(ix, file, focus_handle, cx) - }) - }), - ) - .when( - self.files.len() > OVERFLOW_LIMIT + 1, - |div| { + let selected_file_ix = self + .files + .iter() + .enumerate() + .skip(OVERFLOW_LIMIT) + .find_map(|(ix, (file, _))| { + if file == &self.current_file { + Some(ix) + } else { + None + } + }) + .unwrap_or(OVERFLOW_LIMIT); + + let (file, focus_handle) = &self.files[selected_file_ix]; + + div.child(file_button(selected_file_ix, file, focus_handle, cx)) + .when(self.files.len() > OVERFLOW_LIMIT + 1, |div| { div.child( DropdownMenu::new( "more-files", @@ -1840,18 +1880,19 @@ impl SettingsWindow { .enumerate() .skip(OVERFLOW_LIMIT + 1) { - let (display_name, focus_handle) = if self - .drop_down_file - .is_some_and(|drop_down_ix| drop_down_ix == ix) - { - ix = OVERFLOW_LIMIT; - ( - self.display_name(&self.files[ix].0), - self.files[ix].1.clone(), - ) - } else { - (self.display_name(&file), focus_handle.clone()) - }; + let (display_name, focus_handle) = + if selected_file_ix == ix { + ix = OVERFLOW_LIMIT; + ( + self.display_name(&self.files[ix].0), + self.files[ix].1.clone(), + ) + } else { + ( + self.display_name(&file), + focus_handle.clone(), + ) + }; menu = menu.entry( display_name @@ -1861,9 +1902,7 @@ impl SettingsWindow { let this = this.clone(); move |window, cx| { this.update(cx, |this, cx| { - this.change_file( - ix, true, window, cx, - ); + this.change_file(ix, window, cx); }); focus_handle.focus(window); } @@ -1884,8 +1923,7 @@ impl SettingsWindow { }) .tab_index(0), ) - }, - ) + }) }), ) .child( @@ -3399,7 +3437,6 @@ pub mod test { worktree_root_dirs: HashMap::default(), files: Vec::default(), current_file: crate::SettingsUiFile::User, - drop_down_file: None, pages, search_bar: cx.new(|cx| Editor::single_line(window, cx)), navbar_entry: selected_idx.expect("Must have a selected navbar entry"), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c0e59060bd9ca963343761b77f5b25b18dd8b302..d4547266f43d21b47b5f7efa248602c9c866f391 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1458,7 +1458,7 @@ impl Workspace { }), cx.on_release(move |this, cx| { this.app_state.workspace_store.update(cx, move |store, _| { - store.workspaces.remove(&window_handle.clone()); + store.workspaces.remove(&window_handle); }) }), ];