Allow styling sidebar icons and groups in themes

Max Brunsfeld created

Change summary

assets/themes/cave-dark.json         | 32 +++++++++++++++++++++++++++--
assets/themes/cave-light.json        | 32 +++++++++++++++++++++++++++--
assets/themes/dark.json              | 32 +++++++++++++++++++++++++++--
assets/themes/light.json             | 32 +++++++++++++++++++++++++++--
assets/themes/solarized-dark.json    | 32 +++++++++++++++++++++++++++--
assets/themes/solarized-light.json   | 32 +++++++++++++++++++++++++++--
assets/themes/sulphurpool-dark.json  | 32 +++++++++++++++++++++++++++--
assets/themes/sulphurpool-light.json | 32 +++++++++++++++++++++++++++--
crates/theme/src/theme.rs            | 30 +++++++++++++++-------------
crates/workspace/src/sidebar.rs      | 18 +++++++++++-----
styles/src/styleTree/workspace.ts    |  9 ++++++++
11 files changed, 269 insertions(+), 44 deletions(-)

Detailed changes

assets/themes/cave-dark.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#8b8792",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#8b8792",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#efecf4",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#5852607a"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/cave-light.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#585260",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#585260",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#19171c",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#8b87922e"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/dark.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#9c9c9c",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#9c9c9c",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#ffffff",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#2b2b2b"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/light.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#717171",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#717171",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#000000",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#e3e3e3"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/solarized-dark.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#93a1a1",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#93a1a1",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#fdf6e3",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#586e757a"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/solarized-light.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#586e75",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#586e75",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#002b36",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#93a1a12e"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/sulphurpool-dark.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#979db4",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#979db4",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#f5f7ff",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#5e66877a"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

assets/themes/sulphurpool-light.json 🔗

@@ -212,17 +212,43 @@
       "sidebar_item": {
         "height": 32,
         "icon_color": "#5e6687",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_hover": {
         "height": 32,
         "icon_color": "#5e6687",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5
       },
       "sidebar_item_active": {
         "height": 32,
         "icon_color": "#202746",
-        "icon_size": 18
+        "icon_size": 18,
+        "padding": {
+          "left": 5,
+          "right": 5
+        },
+        "corner_radius": 5,
+        "background": "#979db42e"
+      },
+      "sidebar_items_left": {
+        "margin": {
+          "right": 20
+        }
+      },
+      "sidebar_items_right": {
+        "margin": {
+          "left": 20
+        }
       }
     },
     "titlebar": {

crates/theme/src/theme.rs 🔗

@@ -136,20 +136,6 @@ pub struct FindEditor {
     pub max_width: f32,
 }
 
-#[derive(Deserialize, Default)]
-pub struct Sidebar {
-    pub resize_handle: ContainerStyle,
-}
-
-#[derive(Clone, Copy, Deserialize, Default)]
-pub struct SidebarItem {
-    #[serde(flatten)]
-    pub container: ContainerStyle,
-    pub icon_color: Color,
-    pub icon_size: f32,
-    pub height: f32,
-}
-
 #[derive(Deserialize, Default)]
 pub struct StatusBar {
     #[serde(flatten)]
@@ -161,11 +147,27 @@ pub struct StatusBar {
     pub lsp_message: TextStyle,
     pub auto_update_progress_message: TextStyle,
     pub auto_update_done_message: TextStyle,
+    pub sidebar_items_left: ContainerStyle,
+    pub sidebar_items_right: ContainerStyle,
     pub sidebar_item: SidebarItem,
     pub sidebar_item_active: SidebarItem,
     pub sidebar_item_hover: SidebarItem,
 }
 
+#[derive(Deserialize, Default)]
+pub struct Sidebar {
+    pub resize_handle: ContainerStyle,
+}
+
+#[derive(Clone, Copy, Deserialize, Default)]
+pub struct SidebarItem {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub icon_color: Color,
+    pub icon_size: f32,
+    pub height: f32,
+}
+
 #[derive(Deserialize, Default)]
 pub struct ChatPanel {
     #[serde(flatten)]

crates/workspace/src/sidebar.rs 🔗

@@ -176,22 +176,26 @@ impl View for SidebarButtons {
 
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
         let theme = &cx.global::<Settings>().theme.workspace.status_bar;
-        let style = theme.sidebar_item;
-        let hover_style = theme.sidebar_item_hover;
-        let active_style = theme.sidebar_item_active;
         let sidebar = self.sidebar.read(cx);
+        let item_style = theme.sidebar_item;
+        let hover_item_style = theme.sidebar_item_hover;
+        let active_item_style = theme.sidebar_item_active;
         let active_ix = sidebar.active_item_ix;
         let side = sidebar.side;
+        let group_style = match side {
+            Side::Left => theme.sidebar_items_left,
+            Side::Right => theme.sidebar_items_right,
+        };
         let items = sidebar.items.clone();
         Flex::row()
             .with_children(items.iter().enumerate().map(|(ix, item)| {
                 MouseEventHandler::new::<Self, _, _>(ix, cx, move |state, _| {
                     let style = if Some(ix) == active_ix {
-                        active_style
+                        active_item_style
                     } else if state.hovered {
-                        hover_style
+                        hover_item_style
                     } else {
-                        style
+                        item_style
                     };
                     Svg::new(item.icon_path)
                         .with_color(style.icon_color)
@@ -210,6 +214,8 @@ impl View for SidebarButtons {
                 })
                 .boxed()
             }))
+            .contained()
+            .with_style(group_style)
             .boxed()
     }
 }

styles/src/styleTree/workspace.ts 🔗

@@ -51,6 +51,8 @@ export default function workspace(theme: Theme) {
     height: 32,
     iconColor: iconColor(theme, "secondary"),
     iconSize: 18,
+    padding: { left: 5, right: 5 },
+    cornerRadius: 5,
   };
   const shareIcon = {
     margin: { top: 3, bottom: 2 },
@@ -102,7 +104,14 @@ export default function workspace(theme: Theme) {
       sidebarItemActive: {
         ...sidebarItem,
         iconColor: iconColor(theme, "active"),
+        background: backgroundColor(theme, 300, "active"),
       },
+      sidebarItemsLeft: {
+        margin: { right: 20 }
+      },
+      sidebarItemsRight: {
+        margin: { left: 20 }
+      }
     },
     titlebar: {
       avatarWidth: 18,