Merge branch 'main' into multi-server-completions-tailwind
This commit is contained in:
@@ -0,0 +1,481 @@
|
||||
use gpui::{elements::SafeStylable, Action};
|
||||
|
||||
use crate::{Interactive, Toggleable};
|
||||
|
||||
use self::{action_button::ButtonStyle, disclosure::Disclosable, svg::SvgStyle, toggle::Toggle};
|
||||
|
||||
pub type IconButtonStyle = Interactive<ButtonStyle<SvgStyle>>;
|
||||
pub type ToggleIconButtonStyle = Toggleable<IconButtonStyle>;
|
||||
|
||||
pub trait ComponentExt<C: SafeStylable> {
|
||||
fn toggleable(self, active: bool) -> Toggle<C, ()>;
|
||||
fn disclosable(self, disclosed: Option<bool>, action: Box<dyn Action>) -> Disclosable<C, ()>;
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> ComponentExt<C> for C {
|
||||
fn toggleable(self, active: bool) -> Toggle<C, ()> {
|
||||
Toggle::new(self, active)
|
||||
}
|
||||
|
||||
/// Some(True) => disclosed => content is visible
|
||||
/// Some(false) => closed => content is hidden
|
||||
/// None => No disclosure button, but reserve disclosure spacing
|
||||
fn disclosable(self, disclosed: Option<bool>, action: Box<dyn Action>) -> Disclosable<C, ()> {
|
||||
Disclosable::new(disclosed, self, action)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod disclosure {
|
||||
|
||||
use gpui::{
|
||||
elements::{Component, ContainerStyle, Empty, Flex, ParentElement, SafeStylable},
|
||||
Action, Element,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
use super::{action_button::Button, svg::Svg, IconButtonStyle};
|
||||
|
||||
#[derive(Clone, Default, Deserialize, JsonSchema)]
|
||||
pub struct DisclosureStyle<S> {
|
||||
pub button: IconButtonStyle,
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub spacing: f32,
|
||||
#[serde(flatten)]
|
||||
content: S,
|
||||
}
|
||||
|
||||
impl<S> DisclosureStyle<S> {
|
||||
pub fn button_space(&self) -> f32 {
|
||||
self.spacing + self.button.button_width.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Disclosable<C, S> {
|
||||
disclosed: Option<bool>,
|
||||
action: Box<dyn Action>,
|
||||
id: usize,
|
||||
content: C,
|
||||
style: S,
|
||||
}
|
||||
|
||||
impl Disclosable<(), ()> {
|
||||
pub fn new<C>(
|
||||
disclosed: Option<bool>,
|
||||
content: C,
|
||||
action: Box<dyn Action>,
|
||||
) -> Disclosable<C, ()> {
|
||||
Disclosable {
|
||||
disclosed,
|
||||
content,
|
||||
action,
|
||||
id: 0,
|
||||
style: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Disclosable<C, ()> {
|
||||
pub fn with_id(mut self, id: usize) -> Disclosable<C, ()> {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> SafeStylable for Disclosable<C, ()> {
|
||||
type Style = DisclosureStyle<C::Style>;
|
||||
|
||||
type Output = Disclosable<C, Self::Style>;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self::Output {
|
||||
Disclosable {
|
||||
disclosed: self.disclosed,
|
||||
action: self.action,
|
||||
content: self.content,
|
||||
id: self.id,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> Component for Disclosable<C, DisclosureStyle<C::Style>> {
|
||||
fn render<V: 'static>(self, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
Flex::row()
|
||||
.with_spacing(self.style.spacing)
|
||||
.with_child(if let Some(disclosed) = self.disclosed {
|
||||
Button::dynamic_action(self.action)
|
||||
.with_id(self.id)
|
||||
.with_contents(Svg::new(if disclosed {
|
||||
"icons/file_icons/chevron_down.svg"
|
||||
} else {
|
||||
"icons/file_icons/chevron_right.svg"
|
||||
}))
|
||||
.with_style(self.style.button)
|
||||
.element()
|
||||
.into_any()
|
||||
} else {
|
||||
Empty::new()
|
||||
.into_any()
|
||||
.constrained()
|
||||
// TODO: Why is this optional at all?
|
||||
.with_width(self.style.button.button_width.unwrap())
|
||||
.into_any()
|
||||
})
|
||||
.with_child(
|
||||
self.content
|
||||
.with_style(self.style.content)
|
||||
.render(cx)
|
||||
.flex(1., true),
|
||||
)
|
||||
.align_children_center()
|
||||
.contained()
|
||||
.with_style(self.style.container)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod toggle {
|
||||
use gpui::elements::{Component, SafeStylable};
|
||||
|
||||
use crate::Toggleable;
|
||||
|
||||
pub struct Toggle<C, S> {
|
||||
style: S,
|
||||
active: bool,
|
||||
component: C,
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> Toggle<C, ()> {
|
||||
pub fn new(component: C, active: bool) -> Self {
|
||||
Toggle {
|
||||
active,
|
||||
component,
|
||||
style: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> SafeStylable for Toggle<C, ()> {
|
||||
type Style = Toggleable<C::Style>;
|
||||
|
||||
type Output = Toggle<C, Self::Style>;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self::Output {
|
||||
Toggle {
|
||||
active: self.active,
|
||||
component: self.component,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> Component for Toggle<C, Toggleable<C::Style>> {
|
||||
fn render<V: 'static>(self, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
self.component
|
||||
.with_style(self.style.in_state(self.active).clone())
|
||||
.render(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod action_button {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use gpui::{
|
||||
elements::{Component, ContainerStyle, MouseEventHandler, SafeStylable, TooltipStyle},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
Action, Element, TypeTag,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
use crate::Interactive;
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
pub struct ButtonStyle<C> {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
// TODO: These are incorrect for the intended usage of the buttons.
|
||||
// The size should be constant, but putting them here duplicates them
|
||||
// across the states the buttons can be in
|
||||
pub button_width: Option<f32>,
|
||||
pub button_height: Option<f32>,
|
||||
#[serde(flatten)]
|
||||
contents: C,
|
||||
}
|
||||
|
||||
pub struct Button<C, S> {
|
||||
action: Box<dyn Action>,
|
||||
tooltip: Option<(Cow<'static, str>, TooltipStyle)>,
|
||||
tag: TypeTag,
|
||||
id: usize,
|
||||
contents: C,
|
||||
style: Interactive<S>,
|
||||
}
|
||||
|
||||
impl Button<(), ()> {
|
||||
pub fn dynamic_action(action: Box<dyn Action>) -> Button<(), ()> {
|
||||
Self {
|
||||
contents: (),
|
||||
tag: action.type_tag(),
|
||||
action,
|
||||
style: Interactive::new_blank(),
|
||||
tooltip: None,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action<A: Action + Clone>(action: A) -> Self {
|
||||
Self::dynamic_action(Box::new(action))
|
||||
}
|
||||
|
||||
pub fn with_tooltip(
|
||||
mut self,
|
||||
tooltip: impl Into<Cow<'static, str>>,
|
||||
tooltip_style: TooltipStyle,
|
||||
) -> Self {
|
||||
self.tooltip = Some((tooltip.into(), tooltip_style));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_id(mut self, id: usize) -> Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_contents<C: SafeStylable>(self, contents: C) -> Button<C, ()> {
|
||||
Button {
|
||||
action: self.action,
|
||||
tag: self.tag,
|
||||
style: self.style,
|
||||
tooltip: self.tooltip,
|
||||
id: self.id,
|
||||
contents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> SafeStylable for Button<C, ()> {
|
||||
type Style = Interactive<ButtonStyle<C::Style>>;
|
||||
type Output = Button<C, ButtonStyle<C::Style>>;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self::Output {
|
||||
Button {
|
||||
action: self.action,
|
||||
tag: self.tag,
|
||||
contents: self.contents,
|
||||
tooltip: self.tooltip,
|
||||
id: self.id,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: SafeStylable> Component for Button<C, ButtonStyle<C::Style>> {
|
||||
fn render<V: 'static>(self, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
let mut button = MouseEventHandler::new_dynamic(self.tag, self.id, cx, |state, cx| {
|
||||
let style = self.style.style_for(state);
|
||||
let mut contents = self
|
||||
.contents
|
||||
.with_style(style.contents.to_owned())
|
||||
.render(cx)
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.constrained();
|
||||
|
||||
if let Some(height) = style.button_height {
|
||||
contents = contents.with_height(height);
|
||||
}
|
||||
|
||||
if let Some(width) = style.button_width {
|
||||
contents = contents.with_width(width);
|
||||
}
|
||||
|
||||
contents.into_any()
|
||||
})
|
||||
.on_click(MouseButton::Left, {
|
||||
let action = self.action.boxed_clone();
|
||||
move |_, _, cx| {
|
||||
let window = cx.window();
|
||||
let view = cx.view_id();
|
||||
let action = action.boxed_clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
window.dispatch_action(view, action.as_ref(), &mut cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.into_any();
|
||||
|
||||
if let Some((tooltip, style)) = self.tooltip {
|
||||
button = button
|
||||
.with_dynamic_tooltip(self.tag, 0, tooltip, Some(self.action), style, cx)
|
||||
.into_any()
|
||||
}
|
||||
|
||||
button
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod svg {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use gpui::{
|
||||
elements::{Component, Empty, SafeStylable},
|
||||
Element,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Default, JsonSchema)]
|
||||
pub struct SvgStyle {
|
||||
icon_width: f32,
|
||||
icon_height: f32,
|
||||
color: gpui::color::Color,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SvgStyle {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum IconSize {
|
||||
IconSize { icon_size: f32 },
|
||||
Dimensions { width: f32, height: f32 },
|
||||
IconDimensions { icon_width: f32, icon_height: f32 },
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SvgStyleHelper {
|
||||
#[serde(flatten)]
|
||||
size: IconSize,
|
||||
color: gpui::color::Color,
|
||||
}
|
||||
|
||||
let json = SvgStyleHelper::deserialize(deserializer)?;
|
||||
let color = json.color;
|
||||
|
||||
let result = match json.size {
|
||||
IconSize::IconSize { icon_size } => SvgStyle {
|
||||
icon_width: icon_size,
|
||||
icon_height: icon_size,
|
||||
color,
|
||||
},
|
||||
IconSize::Dimensions { width, height } => SvgStyle {
|
||||
icon_width: width,
|
||||
icon_height: height,
|
||||
color,
|
||||
},
|
||||
IconSize::IconDimensions {
|
||||
icon_width,
|
||||
icon_height,
|
||||
} => SvgStyle {
|
||||
icon_width,
|
||||
icon_height,
|
||||
color,
|
||||
},
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Svg<S> {
|
||||
path: Option<Cow<'static, str>>,
|
||||
style: S,
|
||||
}
|
||||
|
||||
impl Svg<()> {
|
||||
pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
path: Some(path.into()),
|
||||
style: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optional(path: Option<impl Into<Cow<'static, str>>>) -> Self {
|
||||
Self {
|
||||
path: path.map(Into::into),
|
||||
style: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SafeStylable for Svg<()> {
|
||||
type Style = SvgStyle;
|
||||
|
||||
type Output = Svg<SvgStyle>;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self::Output {
|
||||
Svg {
|
||||
path: self.path,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Svg<SvgStyle> {
|
||||
fn render<V: 'static>(self, _: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
if let Some(path) = self.path {
|
||||
gpui::elements::Svg::new(path)
|
||||
.with_color(self.style.color)
|
||||
.constrained()
|
||||
} else {
|
||||
Empty::new().constrained()
|
||||
}
|
||||
.constrained()
|
||||
.with_width(self.style.icon_width)
|
||||
.with_height(self.style.icon_height)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod label {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use gpui::{
|
||||
elements::{Component, LabelStyle, SafeStylable},
|
||||
fonts::TextStyle,
|
||||
Element,
|
||||
};
|
||||
|
||||
pub struct Label<S> {
|
||||
text: Cow<'static, str>,
|
||||
style: S,
|
||||
}
|
||||
|
||||
impl Label<()> {
|
||||
pub fn new(text: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
style: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SafeStylable for Label<()> {
|
||||
type Style = TextStyle;
|
||||
|
||||
type Output = Label<LabelStyle>;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self::Output {
|
||||
Label {
|
||||
text: self.text,
|
||||
style: style.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Label<LabelStyle> {
|
||||
fn render<V: 'static>(self, _: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
gpui::elements::Label::new(self.text, self.style).into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
+149
-26
@@ -1,7 +1,9 @@
|
||||
pub mod components;
|
||||
mod theme_registry;
|
||||
mod theme_settings;
|
||||
pub mod ui;
|
||||
|
||||
use components::{action_button::ButtonStyle, disclosure::DisclosureStyle, ToggleIconButtonStyle};
|
||||
use gpui::{
|
||||
color::Color,
|
||||
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
|
||||
@@ -12,8 +14,8 @@ use schemars::JsonSchema;
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use serde_json::Value;
|
||||
use settings::SettingsStore;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle};
|
||||
use std::{collections::HashMap, ops::Deref, sync::Arc};
|
||||
use ui::{CheckboxStyle, CopilotCTAButton, IconStyle, ModalStyle};
|
||||
|
||||
pub use theme_registry::*;
|
||||
pub use theme_settings::*;
|
||||
@@ -43,11 +45,9 @@ pub struct Theme {
|
||||
pub meta: ThemeMeta,
|
||||
pub workspace: Workspace,
|
||||
pub context_menu: ContextMenu,
|
||||
pub contacts_popover: ContactsPopover,
|
||||
pub contact_list: ContactList,
|
||||
pub toolbar_dropdown_menu: DropdownMenu,
|
||||
pub copilot: Copilot,
|
||||
pub contact_finder: ContactFinder,
|
||||
pub collab_panel: CollabPanel,
|
||||
pub project_panel: ProjectPanel,
|
||||
pub command_palette: CommandPalette,
|
||||
pub picker: Picker,
|
||||
@@ -66,6 +66,7 @@ pub struct Theme {
|
||||
pub feedback: FeedbackStyle,
|
||||
pub welcome: WelcomeStyle,
|
||||
pub titlebar: Titlebar,
|
||||
pub component_test: ComponentTest,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||
@@ -87,8 +88,6 @@ pub struct Workspace {
|
||||
pub dock: Dock,
|
||||
pub status_bar: StatusBar,
|
||||
pub toolbar: Toolbar,
|
||||
pub breadcrumb_height: f32,
|
||||
pub breadcrumbs: Interactive<ContainedText>,
|
||||
pub disconnected_overlay: ContainedText,
|
||||
pub modal: ContainerStyle,
|
||||
pub zoomed_panel_foreground: ContainerStyle,
|
||||
@@ -117,8 +116,8 @@ pub struct Titlebar {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub height: f32,
|
||||
pub menu: TitlebarMenu,
|
||||
pub project_menu_button: Toggleable<Interactive<ContainedText>>,
|
||||
pub project_name_divider: ContainedText,
|
||||
pub git_menu_button: Toggleable<Interactive<ContainedText>>,
|
||||
pub item_spacing: f32,
|
||||
pub face_pile_spacing: f32,
|
||||
@@ -143,6 +142,12 @@ pub struct Titlebar {
|
||||
pub user_menu: UserMenu,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
pub struct TitlebarMenu {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
pub struct UserMenu {
|
||||
pub user_menu_button_online: UserMenuButton,
|
||||
@@ -177,7 +182,7 @@ pub struct CopilotAuth {
|
||||
pub prompting: CopilotAuthPrompting,
|
||||
pub not_authorized: CopilotAuthNotAuthorized,
|
||||
pub authorized: CopilotAuthAuthorized,
|
||||
pub cta_button: ButtonStyle,
|
||||
pub cta_button: CopilotCTAButton,
|
||||
pub header: IconStyle,
|
||||
}
|
||||
|
||||
@@ -191,7 +196,7 @@ pub struct CopilotAuthPrompting {
|
||||
#[derive(Deserialize, Default, Clone, JsonSchema)]
|
||||
pub struct DeviceCode {
|
||||
pub text: TextStyle,
|
||||
pub cta: ButtonStyle,
|
||||
pub cta: CopilotCTAButton,
|
||||
pub left: f32,
|
||||
pub left_container: ContainerStyle,
|
||||
pub right: f32,
|
||||
@@ -211,33 +216,77 @@ pub struct CopilotAuthAuthorized {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
pub struct ContactsPopover {
|
||||
pub struct CollabPanel {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub height: f32,
|
||||
pub width: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
pub struct ContactList {
|
||||
pub disclosure: DisclosureStyle<()>,
|
||||
pub list_empty_state: Toggleable<Interactive<ContainedText>>,
|
||||
pub list_empty_icon: Icon,
|
||||
pub list_empty_label_container: ContainerStyle,
|
||||
pub log_in_button: Interactive<ContainedText>,
|
||||
pub channel_editor: ContainerStyle,
|
||||
pub channel_hash: Icon,
|
||||
pub tabbed_modal: TabbedModal,
|
||||
pub contact_finder: ContactFinder,
|
||||
pub channel_modal: ChannelModal,
|
||||
pub user_query_editor: FieldEditor,
|
||||
pub user_query_editor_height: f32,
|
||||
pub add_contact_button: IconButton,
|
||||
pub header_row: Toggleable<Interactive<ContainedText>>,
|
||||
pub leave_call_button: Toggleable<Interactive<IconButton>>,
|
||||
pub add_contact_button: Toggleable<Interactive<IconButton>>,
|
||||
pub add_channel_button: Toggleable<Interactive<IconButton>>,
|
||||
pub header_row: ContainedText,
|
||||
pub subheader_row: Toggleable<Interactive<ContainedText>>,
|
||||
pub leave_call: Interactive<ContainedText>,
|
||||
pub contact_row: Toggleable<Interactive<ContainerStyle>>,
|
||||
pub channel_row: Toggleable<Interactive<ContainerStyle>>,
|
||||
pub channel_name: ContainedText,
|
||||
pub row_height: f32,
|
||||
pub project_row: Toggleable<Interactive<ProjectRow>>,
|
||||
pub tree_branch: Toggleable<Interactive<TreeBranch>>,
|
||||
pub contact_avatar: ImageStyle,
|
||||
pub channel_avatar: ImageStyle,
|
||||
pub extra_participant_label: ContainedText,
|
||||
pub contact_status_free: ContainerStyle,
|
||||
pub contact_status_busy: ContainerStyle,
|
||||
pub contact_username: ContainedText,
|
||||
pub contact_button: Interactive<IconButton>,
|
||||
pub contact_button_spacing: f32,
|
||||
pub channel_indent: f32,
|
||||
pub disabled_button: IconButton,
|
||||
pub section_icon_size: f32,
|
||||
pub calling_indicator: ContainedText,
|
||||
pub face_overlap: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
pub struct ComponentTest {
|
||||
pub button: Interactive<ButtonStyle<TextStyle>>,
|
||||
pub toggle: Toggleable<Interactive<ButtonStyle<TextStyle>>>,
|
||||
pub disclosure: DisclosureStyle<TextStyle>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
pub struct TabbedModal {
|
||||
pub tab_button: Toggleable<Interactive<ContainedText>>,
|
||||
pub modal: ContainerStyle,
|
||||
pub header: ContainerStyle,
|
||||
pub body: ContainerStyle,
|
||||
pub title: ContainedText,
|
||||
pub picker: Picker,
|
||||
pub max_height: f32,
|
||||
pub max_width: f32,
|
||||
pub row_height: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
pub struct ChannelModal {
|
||||
pub contact_avatar: ImageStyle,
|
||||
pub contact_username: ContainerStyle,
|
||||
pub remove_member_button: ContainedText,
|
||||
pub cancel_invite_button: ContainedText,
|
||||
pub member_icon: IconButton,
|
||||
pub invitee_icon: IconButton,
|
||||
pub member_tag: ContainedText,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
@@ -256,8 +305,6 @@ pub struct TreeBranch {
|
||||
|
||||
#[derive(Deserialize, Default, JsonSchema)]
|
||||
pub struct ContactFinder {
|
||||
pub picker: Picker,
|
||||
pub row_height: f32,
|
||||
pub contact_avatar: ImageStyle,
|
||||
pub contact_username: ContainerStyle,
|
||||
pub contact_button: IconButton,
|
||||
@@ -295,6 +342,7 @@ pub struct TabBar {
|
||||
pub inactive_pane: TabStyles,
|
||||
pub dragged_tab: Tab,
|
||||
pub height: f32,
|
||||
pub nav_button: Interactive<IconButton>,
|
||||
}
|
||||
|
||||
impl TabBar {
|
||||
@@ -359,7 +407,9 @@ pub struct Toolbar {
|
||||
pub container: ContainerStyle,
|
||||
pub height: f32,
|
||||
pub item_spacing: f32,
|
||||
pub nav_button: Interactive<IconButton>,
|
||||
pub toggleable_tool: Toggleable<Interactive<IconButton>>,
|
||||
pub breadcrumb_height: f32,
|
||||
pub breadcrumbs: Interactive<ContainedText>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
@@ -379,12 +429,20 @@ pub struct Search {
|
||||
pub include_exclude_editor: FindEditor,
|
||||
pub invalid_include_exclude_editor: ContainerStyle,
|
||||
pub include_exclude_inputs: ContainedText,
|
||||
pub option_button: Toggleable<Interactive<ContainedText>>,
|
||||
pub action_button: Interactive<ContainedText>,
|
||||
pub option_button: Toggleable<Interactive<IconButton>>,
|
||||
pub option_button_component: ToggleIconButtonStyle,
|
||||
pub action_button: Toggleable<Interactive<ContainedText>>,
|
||||
pub match_background: Color,
|
||||
pub match_index: ContainedText,
|
||||
pub results_status: TextStyle,
|
||||
pub dismiss_button: Interactive<IconButton>,
|
||||
pub major_results_status: TextStyle,
|
||||
pub minor_results_status: TextStyle,
|
||||
pub editor_icon: IconStyle,
|
||||
pub mode_button: Toggleable<Interactive<ContainedText>>,
|
||||
pub nav_button: Toggleable<Interactive<ContainedLabel>>,
|
||||
pub search_bar_row_height: f32,
|
||||
pub search_row_spacing: f32,
|
||||
pub option_button_height: f32,
|
||||
pub modes_container: ContainerStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
@@ -697,6 +755,7 @@ pub struct Editor {
|
||||
pub line_number: Color,
|
||||
pub line_number_active: Color,
|
||||
pub guest_selections: Vec<SelectionStyle>,
|
||||
pub absent_selection: SelectionStyle,
|
||||
pub syntax: Arc<SyntaxTheme>,
|
||||
pub hint: HighlightStyle,
|
||||
pub suggestion: HighlightStyle,
|
||||
@@ -843,12 +902,48 @@ pub struct Interactive<T> {
|
||||
pub disabled: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for Interactive<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.default
|
||||
}
|
||||
}
|
||||
|
||||
impl Interactive<()> {
|
||||
pub fn new_blank() -> Self {
|
||||
Self {
|
||||
default: (),
|
||||
hovered: None,
|
||||
clicked: None,
|
||||
disabled: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
|
||||
pub struct Toggleable<T> {
|
||||
active: T,
|
||||
inactive: T,
|
||||
}
|
||||
|
||||
impl<T> Deref for Toggleable<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inactive
|
||||
}
|
||||
}
|
||||
|
||||
impl Toggleable<()> {
|
||||
pub fn new_blank() -> Self {
|
||||
Self {
|
||||
active: (),
|
||||
inactive: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Toggleable<T> {
|
||||
pub fn new(active: T, inactive: T) -> Self {
|
||||
Self { active, inactive }
|
||||
@@ -863,6 +958,7 @@ impl<T> Toggleable<T> {
|
||||
pub fn active_state(&self) -> &T {
|
||||
self.in_state(true)
|
||||
}
|
||||
|
||||
pub fn inactive_state(&self) -> &T {
|
||||
self.in_state(false)
|
||||
}
|
||||
@@ -883,6 +979,16 @@ impl<T> Interactive<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Toggleable<Interactive<T>> {
|
||||
pub fn style_for(&self, active: bool, state: &mut MouseState) -> &T {
|
||||
self.in_state(active).style_for(state)
|
||||
}
|
||||
|
||||
pub fn default_style(&self) -> &T {
|
||||
&self.inactive.default
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@@ -1046,6 +1152,23 @@ pub struct AssistantStyle {
|
||||
pub api_key_editor: FieldEditor,
|
||||
pub api_key_prompt: ContainedText,
|
||||
pub saved_conversation: SavedConversation,
|
||||
pub inline: InlineAssistantStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
pub struct InlineAssistantStyle {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub editor: FieldEditor,
|
||||
pub disabled_editor: FieldEditor,
|
||||
pub pending_edit_background: Color,
|
||||
pub include_conversation: ToggleIconButtonStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
pub struct Contained<T> {
|
||||
container: ContainerStyle,
|
||||
contained: T,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||
|
||||
+20
-10
@@ -10,7 +10,7 @@ use gpui::{
|
||||
platform,
|
||||
platform::MouseButton,
|
||||
scene::MouseClick,
|
||||
Action, Element, EventContext, MouseState, View, ViewContext,
|
||||
Action, Element, EventContext, MouseState, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@@ -37,7 +37,7 @@ pub fn checkbox<Tag, V, F>(
|
||||
) -> MouseEventHandler<V>
|
||||
where
|
||||
Tag: 'static,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: 'static + Fn(&mut V, bool, &mut EventContext<V>),
|
||||
{
|
||||
let label = Label::new(label, style.label.text.clone())
|
||||
@@ -57,7 +57,7 @@ pub fn checkbox_with_label<Tag, D, V, F>(
|
||||
where
|
||||
Tag: 'static,
|
||||
D: Element<V>,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: 'static + Fn(&mut V, bool, &mut EventContext<V>),
|
||||
{
|
||||
MouseEventHandler::new::<Tag, _>(id, cx, |state, _| {
|
||||
@@ -93,7 +93,7 @@ where
|
||||
.with_cursor_style(platform::CursorStyle::PointingHand)
|
||||
}
|
||||
|
||||
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||
pub fn svg<V: 'static>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||
Svg::new(style.asset.clone())
|
||||
.with_color(style.color)
|
||||
.constrained()
|
||||
@@ -107,11 +107,21 @@ pub struct IconStyle {
|
||||
pub container: ContainerStyle,
|
||||
}
|
||||
|
||||
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
|
||||
impl IconStyle {
|
||||
pub fn width(&self) -> f32 {
|
||||
self.icon.dimensions.width
|
||||
+ self.container.padding.left
|
||||
+ self.container.padding.right
|
||||
+ self.container.margin.left
|
||||
+ self.container.margin.right
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon<V: 'static>(style: &IconStyle) -> Container<V> {
|
||||
svg(&style.icon).contained().with_style(style.container)
|
||||
}
|
||||
|
||||
pub fn keystroke_label<V: View>(
|
||||
pub fn keystroke_label<V: 'static>(
|
||||
label_text: &'static str,
|
||||
label_style: &ContainedText,
|
||||
keystroke_style: &ContainedText,
|
||||
@@ -135,19 +145,19 @@ pub fn keystroke_label<V: View>(
|
||||
.with_style(label_style.container)
|
||||
}
|
||||
|
||||
pub type ButtonStyle = Interactive<ContainedText>;
|
||||
pub type CopilotCTAButton = Interactive<ContainedText>;
|
||||
|
||||
pub fn cta_button<Tag, L, V, F>(
|
||||
label: L,
|
||||
max_width: f32,
|
||||
style: &ButtonStyle,
|
||||
style: &CopilotCTAButton,
|
||||
cx: &mut ViewContext<V>,
|
||||
f: F,
|
||||
) -> MouseEventHandler<V>
|
||||
where
|
||||
Tag: 'static,
|
||||
L: Into<Cow<'static, str>>,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
MouseEventHandler::new::<Tag, _>(0, cx, |state, _| {
|
||||
@@ -186,9 +196,9 @@ pub fn modal<Tag, V, I, D, F>(
|
||||
) -> impl Element<V>
|
||||
where
|
||||
Tag: 'static,
|
||||
V: View,
|
||||
I: Into<Cow<'static, str>>,
|
||||
D: Element<V>,
|
||||
V: 'static,
|
||||
F: FnOnce(&mut gpui::ViewContext<V>) -> D,
|
||||
{
|
||||
const TITLEBAR_HEIGHT: f32 = 28.;
|
||||
|
||||
Reference in New Issue
Block a user