Add width setting for the file finder (#18682)

Isaac Donaldson created

This PR adds the ability to adjust the width of the file finder popup. I
found when searching my projects the default width was not always wide
enough and there was no option to change it.

It allows values `small`, `medium` (default), `large`, `xlarge`, and
`full`

Release Notes:

- Added a setting to adjust the width of the file finder modal


Example Setting:
```json
  "file_finder": {
    "modal_width": "medium"
  },
```

Screenshots can be found in the comments below.

Change summary

assets/settings/default.json                   | 16 ++++++
crates/file_finder/src/file_finder.rs          |  9 ++
crates/file_finder/src/file_finder_settings.rs | 48 ++++++++++++++++++++
docs/src/configuring-zed.md                    |  8 +++
4 files changed, 78 insertions(+), 3 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -580,7 +580,21 @@
   // Settings related to the file finder.
   "file_finder": {
     // Whether to show file icons in the file finder.
-    "file_icons": true
+    "file_icons": true,
+    // Width of the file finder modal. This setting can
+    // take four values.
+    //
+    // 1. Small width:
+    //   "modal_width": "small",
+    // 2. Medium width (default):
+    //   "modal_width": "medium",
+    // 3. Large width:
+    //   "modal_width": "large",
+    // 4. Extra Large width:
+    //   "modal_width": "xlarge"
+    // 5. Fullscreen width:
+    //   "modal_width": "full"
+    "modal_width": "medium"
   },
   // Whether or not to remove any trailing whitespace from lines of a buffer
   // before saving it.

crates/file_finder/src/file_finder.rs 🔗

@@ -14,7 +14,7 @@ use file_finder_settings::FileFinderSettings;
 use file_icons::FileIcons;
 use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
 use gpui::{
-    actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
+    actions, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
     FocusableView, KeyContext, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render,
     Styled, Task, View, ViewContext, VisualContext, WeakView,
 };
@@ -257,9 +257,14 @@ impl FocusableView for FileFinder {
 impl Render for FileFinder {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let key_context = self.picker.read(cx).delegate.key_context(cx);
+
+        let window_max_width: Pixels = cx.viewport_size().width;
+        let modal_choice = FileFinderSettings::get_global(cx).modal_width;
+        let width = modal_choice.calc_width(window_max_width);
+
         v_flex()
             .key_context(key_context)
-            .w(rems(34.))
+            .w(width)
             .on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
             .on_action(cx.listener(Self::handle_select_prev))
             .on_action(cx.listener(Self::handle_open_menu))

crates/file_finder/src/file_finder_settings.rs 🔗

@@ -2,10 +2,13 @@ use anyhow::Result;
 use schemars::JsonSchema;
 use serde_derive::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
+use std::cmp;
+use ui::Pixels;
 
 #[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
 pub struct FileFinderSettings {
     pub file_icons: bool,
+    pub modal_width: FileFinderWidth,
 }
 
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
@@ -14,6 +17,10 @@ pub struct FileFinderSettingsContent {
     ///
     /// Default: true
     pub file_icons: Option<bool>,
+    /// The width of the file finder modal.
+    ///
+    /// Default: "medium"
+    pub modal_width: Option<FileFinderWidth>,
 }
 
 impl Settings for FileFinderSettings {
@@ -25,3 +32,44 @@ impl Settings for FileFinderSettings {
         sources.json_merge()
     }
 }
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "lowercase")]
+pub enum FileFinderWidth {
+    Small,
+    #[default]
+    Medium,
+    Large,
+    XLarge,
+    Full,
+}
+
+impl FileFinderWidth {
+    const MIN_MODAL_WIDTH_PX: f32 = 384.;
+
+    pub fn padding_px(&self) -> Pixels {
+        let padding_val = match self {
+            FileFinderWidth::Small => 1280.,
+            FileFinderWidth::Medium => 1024.,
+            FileFinderWidth::Large => 768.,
+            FileFinderWidth::XLarge => 512.,
+            FileFinderWidth::Full => 0.,
+        };
+
+        Pixels(padding_val)
+    }
+
+    pub fn calc_width(&self, window_width: Pixels) -> Pixels {
+        if self == &FileFinderWidth::Full {
+            return window_width;
+        }
+
+        let min_modal_width_px = Pixels(FileFinderWidth::MIN_MODAL_WIDTH_PX);
+
+        let padding_px = self.padding_px();
+        let width_val = window_width - padding_px;
+        let finder_width = cmp::max(min_modal_width_px, width_val);
+
+        finder_width
+    }
+}

docs/src/configuring-zed.md 🔗

@@ -1416,6 +1416,14 @@ Or to set a `socks5` proxy:
 
 `boolean` values
 
+## File Finder
+
+### Modal Width
+
+- Description: Width of the file finder modal. Can take one of a few values: `small`, `medium`, `large`, `xlarge`, and `full`.
+- Setting: `modal_width`
+- Default: `medium`
+
 ## Preferred Line Length
 
 - Description: The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.