Merge pull request #935 from zed-industries/style-command-palette-active-keystroke

Max Brunsfeld created

Allow the theme to style the keystroke of the active item in the command palette

Change summary

assets/themes/cave-dark.json                  | 126 ++++++--------------
assets/themes/cave-light.json                 | 126 ++++++--------------
assets/themes/dark.json                       | 126 ++++++--------------
assets/themes/light.json                      | 126 ++++++--------------
assets/themes/solarized-dark.json             | 126 ++++++--------------
assets/themes/solarized-light.json            | 126 ++++++--------------
assets/themes/sulphurpool-dark.json           | 126 ++++++--------------
assets/themes/sulphurpool-light.json          | 126 ++++++--------------
crates/command_palette/src/command_palette.rs |  18 +-
crates/diagnostics/src/items.rs               |  14 -
crates/file_finder/src/file_finder.rs         |  14 +-
crates/go_to_line/src/go_to_line.rs           |   4 
crates/outline/src/outline.rs                 |  14 +-
crates/picker/src/picker.rs                   |  48 ++++---
crates/project_symbols/src/project_symbols.rs |  23 ++-
crates/search/src/buffer_search.rs            |  25 ++--
crates/search/src/project_search.rs           |  25 ++--
crates/theme/src/theme.rs                     |  45 ++++---
crates/theme_selector/src/theme_selector.rs   |  14 +-
crates/workspace/src/sidebar.rs               |   8 -
crates/workspace/src/workspace.rs             |  27 +--
styles/src/styleTree/app.ts                   |   4 
styles/src/styleTree/commandPalette.ts        |   3 
styles/src/styleTree/picker.ts                |  40 +++---
styles/src/styleTree/search.ts                |  59 ++++-----
25 files changed, 508 insertions(+), 885 deletions(-)

Detailed changes

assets/themes/cave-dark.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#26232a",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#576ddb",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
-      },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#e2dfe7",
-        "size": 14
       },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#576ddb",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#5852607a",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#e2dfe7",
+          "size": 14
+        }
       },
-      "background": "#5852607a"
+      "hover": {
+        "background": "#58526052"
+      }
     },
     "border": {
       "color": "#19171c",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#efecf4",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#955ae780",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#efecf4",
+      "color": "#8b8792",
       "size": 14,
-      "background": "#655f6d",
+      "background": "#26232a",
       "corner_radius": 4,
       "border": {
-        "color": "#655f6d",
+        "color": "#26232a",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#efecf4",
-      "size": 14,
-      "background": "#655f6d",
-      "corner_radius": 4,
-      "border": {
-        "color": "#655f6d",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#efecf4",
+        "size": 14,
+        "background": "#655f6d",
+        "border": {
+          "color": "#655f6d",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#efecf4",
+        "size": 14,
+        "background": "#655f6d",
+        "border": {
+          "color": "#655f6d",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#efecf4",
-      "size": 14,
-      "background": "#26232a",
-      "corner_radius": 4,
-      "border": {
-        "color": "#655f6d",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#19171c",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#8b8792",
-      "size": 14,
-      "background": "#26232a",
-      "corner_radius": 4,
-      "border": {
-        "color": "#26232a",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/cave-light.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#e2dfe7",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#576ddb",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
-      },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#26232a",
-        "size": 14
       },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#576ddb",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#8b87922e",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#26232a",
+          "size": 14
+        }
       },
-      "background": "#8b87922e"
+      "hover": {
+        "background": "#8b87921f"
+      }
     },
     "border": {
       "color": "#efecf4",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#19171c",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#955ae780",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#19171c",
+      "color": "#585260",
       "size": 14,
-      "background": "#7e7887",
+      "background": "#e2dfe7",
       "corner_radius": 4,
       "border": {
-        "color": "#7e7887",
+        "color": "#e2dfe7",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#19171c",
-      "size": 14,
-      "background": "#7e7887",
-      "corner_radius": 4,
-      "border": {
-        "color": "#7e7887",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#19171c",
+        "size": 14,
+        "background": "#7e7887",
+        "border": {
+          "color": "#7e7887",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#19171c",
+        "size": 14,
+        "background": "#7e7887",
+        "border": {
+          "color": "#7e7887",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#19171c",
-      "size": 14,
-      "background": "#e2dfe7",
-      "corner_radius": 4,
-      "border": {
-        "color": "#7e7887",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#efecf4",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#585260",
-      "size": 14,
-      "background": "#e2dfe7",
-      "corner_radius": 4,
-      "border": {
-        "color": "#e2dfe7",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/dark.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#1c1c1c",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#4f8ff7",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
       },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#f1f1f1",
-        "size": 14
-      },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#4f8ff7",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#2b2b2b",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#f1f1f1",
+          "size": 14
+        }
       },
-      "background": "#2b2b2b"
+      "hover": {
+        "background": "#232323"
+      }
     },
     "border": {
       "color": "#070707",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#ffffff",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#3f15a380",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#ffffff",
+      "color": "#9c9c9c",
       "size": 14,
-      "background": "#232323",
+      "background": "#0e0e0e",
       "corner_radius": 4,
       "border": {
-        "color": "#404040",
+        "color": "#232323",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#ffffff",
-      "size": 14,
-      "background": "#232323",
-      "corner_radius": 4,
-      "border": {
-        "color": "#404040",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#ffffff",
+        "size": 14,
+        "background": "#232323",
+        "border": {
+          "color": "#404040",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#ffffff",
+        "size": 14,
+        "background": "#1c1c1c",
+        "border": {
+          "color": "#404040",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#ffffff",
-      "size": 14,
-      "background": "#0e0e0e",
-      "corner_radius": 4,
-      "border": {
-        "color": "#404040",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#000000",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#9c9c9c",
-      "size": 14,
-      "background": "#0e0e0e",
-      "corner_radius": 4,
-      "border": {
-        "color": "#232323",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/light.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#f8f8f8",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#484bed",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
       },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#2b2b2b",
-        "size": 14
-      },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#484bed",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#e3e3e3",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#2b2b2b",
+          "size": 14
+        }
       },
-      "background": "#e3e3e3"
+      "hover": {
+        "background": "#eaeaea"
+      }
     },
     "border": {
       "color": "#d5d5d5",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#000000",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#fce9b7",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#000000",
+      "color": "#474747",
       "size": 14,
-      "background": "#ffffff",
+      "background": "#f1f1f1",
       "corner_radius": 4,
       "border": {
-        "color": "#e3e3e3",
+        "color": "#d5d5d5",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#000000",
-      "size": 14,
-      "background": "#ffffff",
-      "corner_radius": 4,
-      "border": {
-        "color": "#e3e3e3",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#000000",
+        "size": 14,
+        "background": "#ffffff",
+        "border": {
+          "color": "#e3e3e3",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#000000",
+        "size": 14,
+        "background": "#f8f8f8",
+        "border": {
+          "color": "#e3e3e3",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#000000",
-      "size": 14,
-      "background": "#f1f1f1",
-      "corner_radius": 4,
-      "border": {
-        "color": "#e3e3e3",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#ffffff",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#474747",
-      "size": 14,
-      "background": "#f1f1f1",
-      "corner_radius": 4,
-      "border": {
-        "color": "#d5d5d5",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/solarized-dark.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#073642",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#268bd2",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
-      },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#eee8d5",
-        "size": 14
       },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#268bd2",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#586e757a",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#eee8d5",
+          "size": 14
+        }
       },
-      "background": "#586e757a"
+      "hover": {
+        "background": "#586e7552"
+      }
     },
     "border": {
       "color": "#002b36",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#fdf6e3",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#6c71c480",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#fdf6e3",
+      "color": "#93a1a1",
       "size": 14,
-      "background": "#657b83",
+      "background": "#073642",
       "corner_radius": 4,
       "border": {
-        "color": "#657b83",
+        "color": "#073642",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#fdf6e3",
-      "size": 14,
-      "background": "#657b83",
-      "corner_radius": 4,
-      "border": {
-        "color": "#657b83",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#fdf6e3",
+        "size": 14,
+        "background": "#657b83",
+        "border": {
+          "color": "#657b83",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#fdf6e3",
+        "size": 14,
+        "background": "#657b83",
+        "border": {
+          "color": "#657b83",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#fdf6e3",
-      "size": 14,
-      "background": "#073642",
-      "corner_radius": 4,
-      "border": {
-        "color": "#657b83",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#002b36",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#93a1a1",
-      "size": 14,
-      "background": "#073642",
-      "corner_radius": 4,
-      "border": {
-        "color": "#073642",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/solarized-light.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#eee8d5",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#268bd2",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
-      },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#073642",
-        "size": 14
       },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#268bd2",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#93a1a12e",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#073642",
+          "size": 14
+        }
       },
-      "background": "#93a1a12e"
+      "hover": {
+        "background": "#93a1a11f"
+      }
     },
     "border": {
       "color": "#fdf6e3",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#002b36",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#6c71c480",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#002b36",
+      "color": "#586e75",
       "size": 14,
-      "background": "#839496",
+      "background": "#eee8d5",
       "corner_radius": 4,
       "border": {
-        "color": "#839496",
+        "color": "#eee8d5",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#002b36",
-      "size": 14,
-      "background": "#839496",
-      "corner_radius": 4,
-      "border": {
-        "color": "#839496",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#002b36",
+        "size": 14,
+        "background": "#839496",
+        "border": {
+          "color": "#839496",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#002b36",
+        "size": 14,
+        "background": "#839496",
+        "border": {
+          "color": "#839496",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#002b36",
-      "size": 14,
-      "background": "#eee8d5",
-      "corner_radius": 4,
-      "border": {
-        "color": "#839496",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#fdf6e3",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#586e75",
-      "size": 14,
-      "background": "#eee8d5",
-      "corner_radius": 4,
-      "border": {
-        "color": "#eee8d5",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/sulphurpool-dark.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#293256",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#3d8fd1",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
-      },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#dfe2f1",
-        "size": 14
       },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#3d8fd1",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#5e66877a",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#dfe2f1",
+          "size": 14
+        }
       },
-      "background": "#5e66877a"
+      "hover": {
+        "background": "#5e668752"
+      }
     },
     "border": {
       "color": "#202746",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#f5f7ff",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#6679cc80",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#f5f7ff",
+      "color": "#979db4",
       "size": 14,
-      "background": "#6b7394",
+      "background": "#293256",
       "corner_radius": 4,
       "border": {
-        "color": "#6b7394",
+        "color": "#293256",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#f5f7ff",
-      "size": 14,
-      "background": "#6b7394",
-      "corner_radius": 4,
-      "border": {
-        "color": "#6b7394",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#f5f7ff",
+        "size": 14,
+        "background": "#6b7394",
+        "border": {
+          "color": "#6b7394",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#f5f7ff",
+        "size": 14,
+        "background": "#6b7394",
+        "border": {
+          "color": "#6b7394",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#f5f7ff",
-      "size": 14,
-      "background": "#293256",
-      "corner_radius": 4,
-      "border": {
-        "color": "#6b7394",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#202746",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#979db4",
-      "size": 14,
-      "background": "#293256",
-      "corner_radius": 4,
-      "border": {
-        "color": "#293256",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

assets/themes/sulphurpool-light.json 🔗

@@ -1,5 +1,5 @@
 {
-  "selector": {
+  "picker": {
     "background": "#dfe2f1",
     "corner_radius": 8,
     "padding": 8,
@@ -21,28 +21,18 @@
         "color": "#3d8fd1",
         "weight": "bold",
         "size": 14
-      }
-    },
-    "active_item": {
-      "padding": {
-        "bottom": 4,
-        "left": 12,
-        "right": 12,
-        "top": 4
-      },
-      "corner_radius": 8,
-      "text": {
-        "family": "Zed Sans",
-        "color": "#293256",
-        "size": 14
       },
-      "highlight_text": {
-        "family": "Zed Sans",
-        "color": "#3d8fd1",
-        "weight": "bold",
-        "size": 14
+      "active": {
+        "background": "#979db42e",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#293256",
+          "size": 14
+        }
       },
-      "background": "#979db42e"
+      "hover": {
+        "background": "#979db41f"
+      }
     },
     "border": {
       "color": "#f5f7ff",
@@ -906,6 +896,13 @@
       },
       "margin": {
         "left": 2
+      },
+      "active": {
+        "text": {
+          "family": "Zed Mono",
+          "color": "#202746",
+          "size": 12
+        }
       }
     }
   },
@@ -1307,14 +1304,14 @@
     "match_background": "#6679cc80",
     "tab_icon_spacing": 8,
     "tab_icon_width": 14,
-    "active_hovered_option_button": {
+    "option_button": {
       "family": "Zed Mono",
-      "color": "#202746",
+      "color": "#5e6687",
       "size": 14,
-      "background": "#898ea4",
+      "background": "#dfe2f1",
       "corner_radius": 4,
       "border": {
-        "color": "#898ea4",
+        "color": "#dfe2f1",
         "width": 1
       },
       "margin": {
@@ -1326,27 +1323,26 @@
         "left": 8,
         "right": 8,
         "top": 3
-      }
-    },
-    "active_option_button": {
-      "family": "Zed Mono",
-      "color": "#202746",
-      "size": 14,
-      "background": "#898ea4",
-      "corner_radius": 4,
-      "border": {
-        "color": "#898ea4",
-        "width": 1
       },
-      "margin": {
-        "left": 2,
-        "right": 2
+      "active": {
+        "family": "Zed Mono",
+        "color": "#202746",
+        "size": 14,
+        "background": "#898ea4",
+        "border": {
+          "color": "#898ea4",
+          "width": 1
+        }
       },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
+      "hover": {
+        "family": "Zed Mono",
+        "color": "#202746",
+        "size": 14,
+        "background": "#898ea4",
+        "border": {
+          "color": "#898ea4",
+          "width": 1
+        }
       }
     },
     "editor": {
@@ -1382,27 +1378,6 @@
         "right": 8
       }
     },
-    "hovered_option_button": {
-      "family": "Zed Mono",
-      "color": "#202746",
-      "size": 14,
-      "background": "#dfe2f1",
-      "corner_radius": 4,
-      "border": {
-        "color": "#898ea4",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "invalid_editor": {
       "background": "#f5f7ff",
       "corner_radius": 8,
@@ -1442,27 +1417,6 @@
       "size": 14,
       "padding": 6
     },
-    "option_button": {
-      "family": "Zed Mono",
-      "color": "#5e6687",
-      "size": 14,
-      "background": "#dfe2f1",
-      "corner_radius": 4,
-      "border": {
-        "color": "#dfe2f1",
-        "width": 1
-      },
-      "margin": {
-        "left": 2,
-        "right": 2
-      },
-      "padding": {
-        "bottom": 3,
-        "left": 8,
-        "right": 8,
-        "top": 3
-      }
-    },
     "option_button_group": {
       "padding": {
         "left": 4,

crates/command_palette/src/command_palette.rs 🔗

@@ -1,7 +1,7 @@
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
     actions,
-    elements::{ChildView, Flex, Label, ParentElement},
+    elements::{ChildView, Flex, Label, MouseState, ParentElement},
     keymap::Keystroke,
     Action, Element, Entity, MutableAppContext, View, ViewContext, ViewHandle,
 };
@@ -200,17 +200,19 @@ impl PickerDelegate for CommandPalette {
         }
     }
 
-    fn render_match(&self, ix: usize, selected: bool, cx: &gpui::AppContext) -> gpui::ElementBox {
+    fn render_match(
+        &self,
+        ix: usize,
+        mouse_state: &MouseState,
+        selected: bool,
+        cx: &gpui::AppContext,
+    ) -> gpui::ElementBox {
         let mat = &self.matches[ix];
         let command = &self.actions[mat.candidate_id];
         let settings = cx.global::<Settings>();
         let theme = &settings.theme;
-        let style = if selected {
-            &theme.selector.active_item
-        } else {
-            &theme.selector.item
-        };
-        let key_style = &theme.command_palette.key;
+        let style = theme.picker.item.style_for(mouse_state, selected);
+        let key_style = &theme.command_palette.key.style_for(mouse_state, selected);
         let keystroke_spacing = theme.command_palette.keystroke_spacing;
 
         Flex::row()

crates/diagnostics/src/items.rs 🔗

@@ -95,12 +95,8 @@ impl View for DiagnosticIndicator {
                     .theme
                     .workspace
                     .status_bar
-                    .diagnostic_summary;
-                let style = if state.hovered {
-                    style.hover()
-                } else {
-                    &style.default
-                };
+                    .diagnostic_summary
+                    .style_for(state, false);
 
                 let mut summary_row = Flex::row();
                 if self.summary.error_count > 0 {
@@ -190,11 +186,7 @@ impl View for DiagnosticIndicator {
                 MouseEventHandler::new::<Message, _, _>(1, cx, |state, _| {
                     Label::new(
                         diagnostic.message.split('\n').next().unwrap().to_string(),
-                        if state.hovered {
-                            message_style.hover().text.clone()
-                        } else {
-                            message_style.default.text.clone()
-                        },
+                        message_style.style_for(state, false).text.clone(),
                     )
                     .aligned()
                     .contained()

crates/file_finder/src/file_finder.rs 🔗

@@ -223,14 +223,16 @@ impl PickerDelegate for FileFinder {
         cx.emit(Event::Dismissed);
     }
 
-    fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> ElementBox {
+    fn render_match(
+        &self,
+        ix: usize,
+        mouse_state: &MouseState,
+        selected: bool,
+        cx: &AppContext,
+    ) -> ElementBox {
         let path_match = &self.matches[ix];
         let settings = cx.global::<Settings>();
-        let style = if selected {
-            &settings.theme.selector.active_item
-        } else {
-            &settings.theme.selector.item
-        };
+        let style = settings.theme.picker.item.style_for(mouse_state, selected);
         let (file_name, file_name_positions, full_path, full_path_positions) =
             self.labels_for_match(path_match);
         Flex::column()

crates/go_to_line/src/go_to_line.rs 🔗

@@ -33,7 +33,7 @@ pub enum Event {
 impl GoToLine {
     pub fn new(active_editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) -> Self {
         let line_editor = cx.add_view(|cx| {
-            Editor::single_line(Some(|theme| theme.selector.input_editor.clone()), cx)
+            Editor::single_line(Some(|theme| theme.picker.input_editor.clone()), cx)
         });
         cx.subscribe(&line_editor, Self::on_line_editor_event)
             .detach();
@@ -152,7 +152,7 @@ impl View for GoToLine {
     }
 
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let theme = &cx.global::<Settings>().theme.selector;
+        let theme = &cx.global::<Settings>().theme.picker;
 
         let label = format!(
             "{},{} of {} lines",

crates/outline/src/outline.rs 🔗

@@ -228,14 +228,16 @@ impl PickerDelegate for OutlineView {
         cx.emit(Event::Dismissed);
     }
 
-    fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> ElementBox {
+    fn render_match(
+        &self,
+        ix: usize,
+        mouse_state: &MouseState,
+        selected: bool,
+        cx: &AppContext,
+    ) -> ElementBox {
         let settings = cx.global::<Settings>();
         let string_match = &self.matches[ix];
-        let style = if selected {
-            &settings.theme.selector.active_item
-        } else {
-            &settings.theme.selector.item
-        };
+        let style = settings.theme.picker.item.style_for(mouse_state, selected);
         let outline_item = &self.outline.items[string_match.candidate_id];
 
         Text::new(outline_item.text.clone(), style.label.text.clone())

crates/picker/src/picker.rs 🔗

@@ -1,12 +1,14 @@
 use editor::Editor;
 use gpui::{
     elements::{
-        ChildView, EventHandler, Flex, Label, ParentElement, ScrollTarget, UniformList,
-        UniformListState,
+        ChildView, Flex, Label, MouseEventHandler, MouseState, ParentElement, ScrollTarget,
+        UniformList, UniformListState,
     },
     geometry::vector::{vec2f, Vector2F},
-    keymap, AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, Task,
-    View, ViewContext, ViewHandle, WeakViewHandle,
+    keymap,
+    platform::CursorStyle,
+    AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, Task, View,
+    ViewContext, ViewHandle, WeakViewHandle,
 };
 use settings::Settings;
 use std::cmp;
@@ -29,7 +31,13 @@ pub trait PickerDelegate: View {
     fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> Task<()>;
     fn confirm(&mut self, cx: &mut ViewContext<Self>);
     fn dismiss(&mut self, cx: &mut ViewContext<Self>);
-    fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> ElementBox;
+    fn render_match(
+        &self,
+        ix: usize,
+        state: &MouseState,
+        selected: bool,
+        cx: &AppContext,
+    ) -> ElementBox;
     fn center_selection_after_match_updates(&self) -> bool {
         false
     }
@@ -57,34 +65,34 @@ impl<D: PickerDelegate> View for Picker<D> {
             .with_child(
                 ChildView::new(&self.query_editor)
                     .contained()
-                    .with_style(settings.theme.selector.input_editor.container)
+                    .with_style(settings.theme.picker.input_editor.container)
                     .boxed(),
             )
             .with_child(
                 if match_count == 0 {
                     Label::new(
                         "No matches".into(),
-                        settings.theme.selector.empty.label.clone(),
+                        settings.theme.picker.empty.label.clone(),
                     )
                     .contained()
-                    .with_style(settings.theme.selector.empty.container)
+                    .with_style(settings.theme.picker.empty.container)
                 } else {
                     UniformList::new(
                         self.list_state.clone(),
                         match_count,
                         move |mut range, items, cx| {
-                            let cx = cx.as_ref();
                             let delegate = delegate.upgrade(cx).unwrap();
-                            let delegate = delegate.read(cx);
-                            let selected_ix = delegate.selected_index();
-                            range.end = cmp::min(range.end, delegate.match_count());
+                            let selected_ix = delegate.read(cx).selected_index();
+                            range.end = cmp::min(range.end, delegate.read(cx).match_count());
                             items.extend(range.map(move |ix| {
-                                EventHandler::new(delegate.render_match(ix, ix == selected_ix, cx))
-                                    .on_mouse_down(move |cx| {
-                                        cx.dispatch_action(SelectIndex(ix));
-                                        true
-                                    })
-                                    .boxed()
+                                MouseEventHandler::new::<D, _, _>(ix, cx, |state, cx| {
+                                    delegate
+                                        .read(cx)
+                                        .render_match(ix, state, ix == selected_ix, cx)
+                                })
+                                .on_mouse_down(move |cx| cx.dispatch_action(SelectIndex(ix)))
+                                .with_cursor_style(CursorStyle::PointingHand)
+                                .boxed()
                             }));
                         },
                     )
@@ -95,7 +103,7 @@ impl<D: PickerDelegate> View for Picker<D> {
                 .boxed(),
             )
             .contained()
-            .with_style(settings.theme.selector.container)
+            .with_style(settings.theme.picker.container)
             .constrained()
             .with_max_width(self.max_size.x())
             .with_max_height(self.max_size.y())
@@ -126,7 +134,7 @@ impl<D: PickerDelegate> Picker<D> {
 
     pub fn new(delegate: WeakViewHandle<D>, cx: &mut ViewContext<Self>) -> Self {
         let query_editor = cx.add_view(|cx| {
-            Editor::single_line(Some(|theme| theme.selector.input_editor.clone()), cx)
+            Editor::single_line(Some(|theme| theme.picker.input_editor.clone()), cx)
         });
         cx.subscribe(&query_editor, Self::on_query_editor_event)
             .detach();

crates/project_symbols/src/project_symbols.rs 🔗

@@ -220,14 +220,17 @@ impl PickerDelegate for ProjectSymbolsView {
         Task::ready(())
     }
 
-    fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> ElementBox {
+    fn render_match(
+        &self,
+        ix: usize,
+        mouse_state: &MouseState,
+        selected: bool,
+        cx: &AppContext,
+    ) -> ElementBox {
         let string_match = &self.matches[ix];
         let settings = cx.global::<Settings>();
-        let style = if selected {
-            &settings.theme.selector.active_item
-        } else {
-            &settings.theme.selector.item
-        };
+        let style = &settings.theme.picker.item;
+        let current_style = style.style_for(mouse_state, selected);
         let symbol = &self.symbols[string_match.candidate_id];
         let syntax_runs = styled_runs_for_code_label(&symbol.label, &settings.theme.editor.syntax);
 
@@ -246,11 +249,11 @@ impl PickerDelegate for ProjectSymbolsView {
 
         Flex::column()
             .with_child(
-                Text::new(symbol.label.text.clone(), style.label.text.clone())
+                Text::new(symbol.label.text.clone(), current_style.label.text.clone())
                     .with_soft_wrap(false)
                     .with_highlights(combine_syntax_and_fuzzy_match_highlights(
                         &symbol.label.text,
-                        style.label.text.clone().into(),
+                        current_style.label.text.clone().into(),
                         syntax_runs,
                         &string_match.positions,
                     ))
@@ -259,10 +262,10 @@ impl PickerDelegate for ProjectSymbolsView {
             .with_child(
                 // Avoid styling the path differently when it is selected, since
                 // the symbol's syntax highlighting doesn't change when selected.
-                Label::new(path.to_string(), settings.theme.selector.item.label.clone()).boxed(),
+                Label::new(path.to_string(), style.default.label.clone()).boxed(),
             )
             .contained()
-            .with_style(style.container)
+            .with_style(current_style.container)
             .boxed()
     }
 }

crates/search/src/buffer_search.rs 🔗

@@ -281,13 +281,12 @@ impl BufferSearchBar {
     ) -> ElementBox {
         let is_active = self.is_search_option_enabled(search_option);
         MouseEventHandler::new::<Self, _, _>(search_option as usize, cx, |state, cx| {
-            let theme = &cx.global::<Settings>().theme.search;
-            let style = match (is_active, state.hovered) {
-                (false, false) => &theme.option_button,
-                (false, true) => &theme.hovered_option_button,
-                (true, false) => &theme.active_option_button,
-                (true, true) => &theme.active_hovered_option_button,
-            };
+            let style = &cx
+                .global::<Settings>()
+                .theme
+                .search
+                .option_button
+                .style_for(state, is_active);
             Label::new(icon.to_string(), style.text.clone())
                 .contained()
                 .with_style(style.container)
@@ -306,12 +305,12 @@ impl BufferSearchBar {
     ) -> ElementBox {
         enum NavButton {}
         MouseEventHandler::new::<NavButton, _, _>(direction as usize, cx, |state, cx| {
-            let theme = &cx.global::<Settings>().theme.search;
-            let style = if state.hovered {
-                &theme.hovered_option_button
-            } else {
-                &theme.option_button
-            };
+            let style = &cx
+                .global::<Settings>()
+                .theme
+                .search
+                .option_button
+                .style_for(state, false);
             Label::new(icon.to_string(), style.text.clone())
                 .contained()
                 .with_style(style.container)

crates/search/src/project_search.rs 🔗

@@ -655,12 +655,12 @@ impl ProjectSearchBar {
     ) -> ElementBox {
         enum NavButton {}
         MouseEventHandler::new::<NavButton, _, _>(direction as usize, cx, |state, cx| {
-            let theme = &cx.global::<Settings>().theme.search;
-            let style = if state.hovered {
-                &theme.hovered_option_button
-            } else {
-                &theme.option_button
-            };
+            let style = &cx
+                .global::<Settings>()
+                .theme
+                .search
+                .option_button
+                .style_for(state, false);
             Label::new(icon.to_string(), style.text.clone())
                 .contained()
                 .with_style(style.container)
@@ -682,13 +682,12 @@ impl ProjectSearchBar {
     ) -> ElementBox {
         let is_active = self.is_option_enabled(option, cx);
         MouseEventHandler::new::<ProjectSearchBar, _, _>(option as usize, cx, |state, cx| {
-            let theme = &cx.global::<Settings>().theme.search;
-            let style = match (is_active, state.hovered) {
-                (false, false) => &theme.option_button,
-                (false, true) => &theme.hovered_option_button,
-                (true, false) => &theme.active_option_button,
-                (true, true) => &theme.active_hovered_option_button,
-            };
+            let style = &cx
+                .global::<Settings>()
+                .theme
+                .search
+                .option_button
+                .style_for(state, is_active);
             Label::new(icon.to_string(), style.text.clone())
                 .contained()
                 .with_style(style.container)

crates/theme/src/theme.rs 🔗

@@ -2,7 +2,7 @@ mod theme_registry;
 
 use gpui::{
     color::Color,
-    elements::{ContainerStyle, ImageStyle, LabelStyle},
+    elements::{ContainerStyle, ImageStyle, LabelStyle, MouseState},
     fonts::{HighlightStyle, TextStyle},
     Border,
 };
@@ -23,7 +23,7 @@ pub struct Theme {
     pub contacts_panel: ContactsPanel,
     pub project_panel: ProjectPanel,
     pub command_palette: CommandPalette,
-    pub selector: Selector,
+    pub picker: Picker,
     pub editor: Editor,
     pub search: Search,
     pub project_diagnostics: ProjectDiagnostics,
@@ -114,10 +114,7 @@ pub struct Search {
     pub editor: FindEditor,
     pub invalid_editor: ContainerStyle,
     pub option_button_group: ContainerStyle,
-    pub option_button: ContainedText,
-    pub active_option_button: ContainedText,
-    pub hovered_option_button: ContainedText,
-    pub active_hovered_option_button: ContainedText,
+    pub option_button: Interactive<ContainedText>,
     pub match_background: Color,
     pub match_index: ContainedText,
     pub results_status: TextStyle,
@@ -229,7 +226,7 @@ pub struct ProjectPanelEntry {
 
 #[derive(Debug, Deserialize, Default)]
 pub struct CommandPalette {
-    pub key: ContainedLabel,
+    pub key: Interactive<ContainedLabel>,
     pub keystroke_spacing: f32,
 }
 
@@ -288,13 +285,12 @@ pub struct ChannelName {
 }
 
 #[derive(Deserialize, Default)]
-pub struct Selector {
+pub struct Picker {
     #[serde(flatten)]
     pub container: ContainerStyle,
     pub empty: ContainedLabel,
     pub input_editor: FieldEditor,
-    pub item: ContainedLabel,
-    pub active_item: ContainedLabel,
+    pub item: Interactive<ContainedLabel>,
 }
 
 #[derive(Clone, Debug, Deserialize, Default)]
@@ -410,7 +406,7 @@ pub struct FieldEditor {
     pub selection: SelectionStyle,
 }
 
-#[derive(Default, Clone, Copy)]
+#[derive(Debug, Default, Clone, Copy)]
 pub struct Interactive<T> {
     pub default: T,
     pub hover: Option<T>,
@@ -419,16 +415,23 @@ pub struct Interactive<T> {
 }
 
 impl<T> Interactive<T> {
-    pub fn active(&self) -> &T {
-        self.active.as_ref().unwrap_or(&self.default)
-    }
-
-    pub fn hover(&self) -> &T {
-        self.hover.as_ref().unwrap_or(&self.default)
-    }
-
-    pub fn active_hover(&self) -> &T {
-        self.active_hover.as_ref().unwrap_or(self.active())
+    pub fn style_for(&self, state: &MouseState, active: bool) -> &T {
+        if active {
+            if state.hovered {
+                self.active_hover
+                    .as_ref()
+                    .or(self.active.as_ref())
+                    .unwrap_or(&self.default)
+            } else {
+                self.active.as_ref().unwrap_or(&self.default)
+            }
+        } else {
+            if state.hovered {
+                self.hover.as_ref().unwrap_or(&self.default)
+            } else {
+                &self.default
+            }
+        }
     }
 }
 

crates/theme_selector/src/theme_selector.rs 🔗

@@ -203,15 +203,17 @@ impl PickerDelegate for ThemeSelector {
         })
     }
 
-    fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> ElementBox {
+    fn render_match(
+        &self,
+        ix: usize,
+        mouse_state: &MouseState,
+        selected: bool,
+        cx: &AppContext,
+    ) -> ElementBox {
         let settings = cx.global::<Settings>();
         let theme = &settings.theme;
         let theme_match = &self.matches[ix];
-        let style = if selected {
-            &theme.selector.active_item
-        } else {
-            &theme.selector.item
-        };
+        let style = theme.picker.item.style_for(mouse_state, selected);
 
         Label::new(theme_match.string.clone(), style.label.clone())
             .with_highlights(theme_match.positions.clone())

crates/workspace/src/sidebar.rs 🔗

@@ -193,13 +193,7 @@ impl View for SidebarButtons {
         Flex::row()
             .with_children(items.iter().enumerate().map(|(ix, item)| {
                 MouseEventHandler::new::<Self, _, _>(ix, cx, move |state, _| {
-                    let style = if Some(ix) == active_ix {
-                        item_style.active()
-                    } else if state.hovered {
-                        item_style.hover()
-                    } else {
-                        &item_style.default
-                    };
+                    let style = item_style.style_for(state, Some(ix) == active_ix);
                     Svg::new(item.icon_path)
                         .with_color(style.icon_color)
                         .constrained()

crates/workspace/src/workspace.rs 🔗

@@ -1574,11 +1574,11 @@ impl Workspace {
         } else {
             Some(
                 MouseEventHandler::new::<Authenticate, _, _>(0, cx, |state, _| {
-                    let style = if state.hovered {
-                        &theme.workspace.titlebar.sign_in_prompt.hover()
-                    } else {
-                        &theme.workspace.titlebar.sign_in_prompt.default
-                    };
+                    let style = theme
+                        .workspace
+                        .titlebar
+                        .sign_in_prompt
+                        .style_for(state, false);
                     Label::new("Sign in".to_string(), style.text.clone())
                         .contained()
                         .with_style(style.container)
@@ -1649,18 +1649,11 @@ impl Workspace {
         {
             Some(
                 MouseEventHandler::new::<ToggleShare, _, _>(0, cx, |state, cx| {
-                    let style = &theme.workspace.titlebar.share_icon;
-                    let style = if self.project().read(cx).is_shared() {
-                        if state.hovered {
-                            style.active_hover()
-                        } else {
-                            &style.active()
-                        }
-                    } else if state.hovered {
-                        &style.active()
-                    } else {
-                        &style.default
-                    };
+                    let style = &theme
+                        .workspace
+                        .titlebar
+                        .share_icon
+                        .style_for(state, self.project().read(cx).is_shared());
                     Svg::new("icons/share.svg")
                         .with_color(style.color)
                         .constrained()

styles/src/styleTree/app.ts 🔗

@@ -6,7 +6,7 @@ import commandPalette from "./commandPalette";
 import editor from "./editor";
 import projectPanel from "./projectPanel";
 import search from "./search";
-import selectorModal from "./selectorModal";
+import picker from "./picker";
 import workspace from "./workspace";
 import projectDiagnostics from "./projectDiagnostics";
 
@@ -16,7 +16,7 @@ export const panel = {
 
 export default function app(theme: Theme): Object {
   return {
-    selector: selectorModal(theme),
+    picker: picker(theme),
     workspace: workspace(theme),
     editor: editor(theme),
     projectDiagnostics: projectDiagnostics(theme),

styles/src/styleTree/commandPalette.ts 🔗

@@ -18,6 +18,9 @@ export default function commandPalette(theme: Theme) {
       margin: {
         left: 2
       },
+      active: {
+        text: text(theme, "mono", "active", { size: "xs" }),
+      }
     }
   }
 }

styles/src/styleTree/selectorModal.ts → styles/src/styleTree/picker.ts 🔗

@@ -1,31 +1,29 @@
 import Theme from "../themes/theme";
 import { backgroundColor, border, player, shadow, text } from "./components";
 
-export default function selectorModal(theme: Theme): Object {
-  const item = {
-    padding: {
-      bottom: 4,
-      left: 12,
-      right: 12,
-      top: 4,
-    },
-    cornerRadius: 8,
-    text: text(theme, "sans", "secondary"),
-    highlightText: text(theme, "sans", "feature", { weight: "bold" }),
-  };
-
-  const activeItem = {
-    ...item,
-    background: backgroundColor(theme, 300, "active"),
-    text: text(theme, "sans", "primary"),
-  };
-
+export default function picker(theme: Theme) {
   return {
     background: backgroundColor(theme, 300),
     cornerRadius: 8,
     padding: 8,
-    item,
-    activeItem,
+    item: {
+      padding: {
+        bottom: 4,
+        left: 12,
+        right: 12,
+        top: 4,
+      },
+      cornerRadius: 8,
+      text: text(theme, "sans", "secondary"),
+      highlightText: text(theme, "sans", "feature", { weight: "bold" }),
+      active: {
+        background: backgroundColor(theme, 300, "active"),
+        text: text(theme, "sans", "primary"),
+      },
+      hover: {
+        background: backgroundColor(theme, 300, "hovered"),
+      }
+    },
     border: border(theme, "primary"),
     empty: {
       text: text(theme, "sans", "placeholder"),

styles/src/styleTree/search.ts 🔗

@@ -2,23 +2,6 @@ import Theme from "../themes/theme";
 import { backgroundColor, border, player, text } from "./components";
 
 export default function search(theme: Theme) {
-  const optionButton = {
-    ...text(theme, "mono", "secondary"),
-    background: backgroundColor(theme, "on500"),
-    cornerRadius: 4,
-    border: border(theme, "secondary"),
-    margin: {
-      left: 2,
-      right: 2,
-    },
-    padding: {
-      bottom: 3,
-      left: 8,
-      right: 8,
-      top: 3,
-    },
-  };
-
   const editor = {
     background: backgroundColor(theme, 500),
     cornerRadius: 8,
@@ -43,24 +26,33 @@ export default function search(theme: Theme) {
     matchBackground: theme.editor.highlight.match.value,
     tabIconSpacing: 8,
     tabIconWidth: 14,
-    activeHoveredOptionButton: {
-      ...optionButton,
-      ...text(theme, "mono", "active"),
-      background: backgroundColor(theme, "on500", "active"),
-      border: border(theme, "muted"),
-    },
-    activeOptionButton: {
-      ...optionButton,
-      ...text(theme, "mono", "active"),
-      background: backgroundColor(theme, "on500", "active"),
-      border: border(theme, "muted"),
+    optionButton: {
+      ...text(theme, "mono", "secondary"),
+      background: backgroundColor(theme, "on500"),
+      cornerRadius: 4,
+      border: border(theme, "secondary"),
+      margin: {
+        left: 2,
+        right: 2,
+      },
+      padding: {
+        bottom: 3,
+        left: 8,
+        right: 8,
+        top: 3,
+      },
+      active: {
+        ...text(theme, "mono", "active"),
+        background: backgroundColor(theme, "on500", "active"),
+        border: border(theme, "muted"),
+      },
+      hover: {
+        ...text(theme, "mono", "active"),
+        background: backgroundColor(theme, "on500", "hovered"),
+        border: border(theme, "muted"),
+      }
     },
     editor,
-    hoveredOptionButton: {
-      ...optionButton,
-      ...text(theme, "mono", "active"),
-      border: border(theme, "muted"),
-    },
     invalidEditor: {
       ...editor,
       border: border(theme, "error"),
@@ -69,7 +61,6 @@ export default function search(theme: Theme) {
       ...text(theme, "mono", "muted"),
       padding: 6,
     },
-    optionButton,
     optionButtonGroup: {
       padding: {
         left: 4,