Add disclosable components into channels
Rename components to more closely match their purpose
This commit is contained in:
parent
2d37128693
commit
bd3ab82dac
10 changed files with 359 additions and 194 deletions
|
@ -114,6 +114,16 @@ impl ChannelStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_children(&self, channel_id: ChannelId) -> bool {
|
||||||
|
self.channel_paths.iter().any(|path| {
|
||||||
|
if let Some(ix) = path.iter().position(|id| *id == channel_id) {
|
||||||
|
path.len() > ix + 1
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn channel_count(&self) -> usize {
|
pub fn channel_count(&self) -> usize {
|
||||||
self.channel_paths.len()
|
self.channel_paths.len()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{
|
elements::{
|
||||||
Canvas, ChildView, Empty, Flex, GeneralComponent, GeneralStyleableComponent, Image, Label,
|
Canvas, ChildView, Component, Empty, Flex, Image, Label, List, ListOffset, ListState,
|
||||||
List, ListOffset, ListState, MouseEventHandler, Orientation, OverlayPositionMode, Padding,
|
MouseEventHandler, Orientation, OverlayPositionMode, Padding, ParentElement, Stack,
|
||||||
ParentElement, Stack, Svg,
|
StyleableComponent, Svg,
|
||||||
},
|
},
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
|
@ -36,7 +36,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use staff_mode::StaffMode;
|
use staff_mode::StaffMode;
|
||||||
use std::{borrow::Cow, mem, sync::Arc};
|
use std::{borrow::Cow, mem, sync::Arc};
|
||||||
use theme::IconButton;
|
use theme::{components::ComponentExt, IconButton};
|
||||||
use util::{iife, ResultExt, TryFutureExt};
|
use util::{iife, ResultExt, TryFutureExt};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
dock::{DockPosition, Panel},
|
dock::{DockPosition, Panel},
|
||||||
|
@ -44,10 +44,7 @@ use workspace::{
|
||||||
Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::face_pile::FacePile;
|
||||||
collab_panel::components::{DisclosureExt, DisclosureStyle},
|
|
||||||
face_pile::FacePile,
|
|
||||||
};
|
|
||||||
use channel_modal::ChannelModal;
|
use channel_modal::ChannelModal;
|
||||||
|
|
||||||
use self::contact_finder::ContactFinder;
|
use self::contact_finder::ContactFinder;
|
||||||
|
@ -57,6 +54,11 @@ struct RemoveChannel {
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
struct ToggleCollapsed {
|
||||||
|
channel_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
struct NewChannel {
|
struct NewChannel {
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
|
@ -86,7 +88,8 @@ impl_actions!(
|
||||||
NewChannel,
|
NewChannel,
|
||||||
InviteMembers,
|
InviteMembers,
|
||||||
ManageMembers,
|
ManageMembers,
|
||||||
RenameChannel
|
RenameChannel,
|
||||||
|
ToggleCollapsed
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -109,6 +112,7 @@ pub fn init(_client: Arc<Client>, cx: &mut AppContext) {
|
||||||
cx.add_action(CollabPanel::manage_members);
|
cx.add_action(CollabPanel::manage_members);
|
||||||
cx.add_action(CollabPanel::rename_selected_channel);
|
cx.add_action(CollabPanel::rename_selected_channel);
|
||||||
cx.add_action(CollabPanel::rename_channel);
|
cx.add_action(CollabPanel::rename_channel);
|
||||||
|
cx.add_action(CollabPanel::toggle_channel_collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -151,6 +155,7 @@ pub struct CollabPanel {
|
||||||
list_state: ListState<Self>,
|
list_state: ListState<Self>,
|
||||||
subscriptions: Vec<Subscription>,
|
subscriptions: Vec<Subscription>,
|
||||||
collapsed_sections: Vec<Section>,
|
collapsed_sections: Vec<Section>,
|
||||||
|
collapsed_channels: Vec<ChannelId>,
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
context_menu_on_selected: bool,
|
context_menu_on_selected: bool,
|
||||||
}
|
}
|
||||||
|
@ -402,6 +407,7 @@ impl CollabPanel {
|
||||||
subscriptions: Vec::default(),
|
subscriptions: Vec::default(),
|
||||||
match_candidates: Vec::default(),
|
match_candidates: Vec::default(),
|
||||||
collapsed_sections: vec![Section::Offline],
|
collapsed_sections: vec![Section::Offline],
|
||||||
|
collapsed_channels: Vec::default(),
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
client: workspace.app_state().client.clone(),
|
client: workspace.app_state().client.clone(),
|
||||||
context_menu_on_selected: true,
|
context_menu_on_selected: true,
|
||||||
|
@ -661,10 +667,24 @@ impl CollabPanel {
|
||||||
self.entries.push(ListEntry::ChannelEditor { depth: 0 });
|
self.entries.push(ListEntry::ChannelEditor { depth: 0 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut collapse_depth = None;
|
||||||
for mat in matches {
|
for mat in matches {
|
||||||
let (depth, channel) =
|
let (depth, channel) =
|
||||||
channel_store.channel_at_index(mat.candidate_id).unwrap();
|
channel_store.channel_at_index(mat.candidate_id).unwrap();
|
||||||
|
|
||||||
|
if collapse_depth.is_none() && self.is_channel_collapsed(channel.id) {
|
||||||
|
collapse_depth = Some(depth);
|
||||||
|
} else if let Some(collapsed_depth) = collapse_depth {
|
||||||
|
if depth > collapsed_depth {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self.is_channel_collapsed(channel.id) {
|
||||||
|
collapse_depth = Some(depth);
|
||||||
|
} else {
|
||||||
|
collapse_depth = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match &self.channel_editing_state {
|
match &self.channel_editing_state {
|
||||||
Some(ChannelEditingState::Create { parent_id, .. })
|
Some(ChannelEditingState::Create { parent_id, .. })
|
||||||
if *parent_id == Some(channel.id) =>
|
if *parent_id == Some(channel.id) =>
|
||||||
|
@ -1483,6 +1503,11 @@ impl CollabPanel {
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<Self> {
|
) -> AnyElement<Self> {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
|
.with_child(
|
||||||
|
Empty::new()
|
||||||
|
.constrained()
|
||||||
|
.with_width(theme.collab_panel.disclosure.button_space()),
|
||||||
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
Svg::new("icons/hash.svg")
|
Svg::new("icons/hash.svg")
|
||||||
.with_color(theme.collab_panel.channel_hash.color)
|
.with_color(theme.collab_panel.channel_hash.color)
|
||||||
|
@ -1541,6 +1566,10 @@ impl CollabPanel {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> AnyElement<Self> {
|
) -> AnyElement<Self> {
|
||||||
let channel_id = channel.id;
|
let channel_id = channel.id;
|
||||||
|
let has_children = self.channel_store.read(cx).has_children(channel_id);
|
||||||
|
let disclosed =
|
||||||
|
has_children.then(|| !self.collapsed_channels.binary_search(&channel_id).is_ok());
|
||||||
|
|
||||||
let is_active = iife!({
|
let is_active = iife!({
|
||||||
let call_channel = ActiveCall::global(cx)
|
let call_channel = ActiveCall::global(cx)
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -1554,7 +1583,7 @@ impl CollabPanel {
|
||||||
const FACEPILE_LIMIT: usize = 3;
|
const FACEPILE_LIMIT: usize = 3;
|
||||||
|
|
||||||
MouseEventHandler::new::<Channel, _>(channel.id as usize, cx, |state, cx| {
|
MouseEventHandler::new::<Channel, _>(channel.id as usize, cx, |state, cx| {
|
||||||
Flex::row()
|
Flex::<Self>::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Svg::new("icons/hash.svg")
|
Svg::new("icons/hash.svg")
|
||||||
.with_color(theme.channel_hash.color)
|
.with_color(theme.channel_hash.color)
|
||||||
|
@ -1603,6 +1632,14 @@ impl CollabPanel {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.align_children_center()
|
.align_children_center()
|
||||||
|
.styleable_component()
|
||||||
|
.disclosable(
|
||||||
|
disclosed,
|
||||||
|
Box::new(ToggleCollapsed { channel_id }),
|
||||||
|
channel_id as usize,
|
||||||
|
)
|
||||||
|
.with_style(theme.disclosure.clone())
|
||||||
|
.element()
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_height(theme.row_height)
|
.with_height(theme.row_height)
|
||||||
.contained()
|
.contained()
|
||||||
|
@ -1619,17 +1656,6 @@ impl CollabPanel {
|
||||||
this.deploy_channel_context_menu(Some(e.position), channel_id, cx);
|
this.deploy_channel_context_menu(Some(e.position), channel_id, cx);
|
||||||
})
|
})
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.dynamic_component()
|
|
||||||
.stylable()
|
|
||||||
.disclosable(true, Box::new(RemoveChannel { channel_id: 0 }))
|
|
||||||
.with_style({
|
|
||||||
fn style() -> DisclosureStyle<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
style()
|
|
||||||
})
|
|
||||||
.element()
|
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2024,6 +2050,24 @@ impl CollabPanel {
|
||||||
self.update_entries(false, cx);
|
self.update_entries(false, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_channel_collapsed(&mut self, action: &ToggleCollapsed, cx: &mut ViewContext<Self>) {
|
||||||
|
let channel_id = action.channel_id;
|
||||||
|
match self.collapsed_channels.binary_search(&channel_id) {
|
||||||
|
Ok(ix) => {
|
||||||
|
self.collapsed_channels.remove(ix);
|
||||||
|
}
|
||||||
|
Err(ix) => {
|
||||||
|
self.collapsed_channels.insert(ix, channel_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.update_entries(false, cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_channel_collapsed(&self, channel: ChannelId) -> bool {
|
||||||
|
self.collapsed_channels.binary_search(&channel).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
fn leave_call(cx: &mut ViewContext<Self>) {
|
fn leave_call(cx: &mut ViewContext<Self>) {
|
||||||
ActiveCall::global(cx)
|
ActiveCall::global(cx)
|
||||||
.update(cx, |call, cx| call.hang_up(cx))
|
.update(cx, |call, cx| call.hang_up(cx))
|
||||||
|
@ -2537,87 +2581,3 @@ fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Elemen
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
}
|
}
|
||||||
|
|
||||||
mod components {
|
|
||||||
|
|
||||||
use gpui::{
|
|
||||||
elements::{Empty, Flex, GeneralComponent, GeneralStyleableComponent, ParentElement},
|
|
||||||
Action, Element,
|
|
||||||
};
|
|
||||||
use theme::components::{
|
|
||||||
action_button::ActionButton, svg::Svg, ComponentExt, ToggleIconButtonStyle,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct DisclosureStyle<S> {
|
|
||||||
disclosure: ToggleIconButtonStyle,
|
|
||||||
spacing: f32,
|
|
||||||
content: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Disclosable<C, S> {
|
|
||||||
disclosed: bool,
|
|
||||||
action: Box<dyn Action>,
|
|
||||||
content: C,
|
|
||||||
style: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Disclosable<(), ()> {
|
|
||||||
fn new<C>(disclosed: bool, content: C, action: Box<dyn Action>) -> Disclosable<C, ()> {
|
|
||||||
Disclosable {
|
|
||||||
disclosed,
|
|
||||||
content,
|
|
||||||
action,
|
|
||||||
style: (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> GeneralStyleableComponent 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,
|
|
||||||
style,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> GeneralComponent for Disclosable<C, DisclosureStyle<C::Style>> {
|
|
||||||
fn render<V: gpui::View>(
|
|
||||||
self,
|
|
||||||
v: &mut V,
|
|
||||||
cx: &mut gpui::ViewContext<V>,
|
|
||||||
) -> gpui::AnyElement<V> {
|
|
||||||
Flex::row()
|
|
||||||
.with_child(
|
|
||||||
ActionButton::new_dynamic(self.action)
|
|
||||||
.with_contents(Svg::new("path"))
|
|
||||||
.toggleable(self.disclosed)
|
|
||||||
.with_style(self.style.disclosure)
|
|
||||||
.element(),
|
|
||||||
)
|
|
||||||
.with_child(Empty::new().constrained().with_width(self.style.spacing))
|
|
||||||
.with_child(self.content.with_style(self.style.content).render(v, cx))
|
|
||||||
.align_children_center()
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DisclosureExt {
|
|
||||||
fn disclosable(self, disclosed: bool, action: Box<dyn Action>) -> Disclosable<Self, ()>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> DisclosureExt for C {
|
|
||||||
fn disclosable(self, disclosed: bool, action: Box<dyn Action>) -> Disclosable<Self, ()> {
|
|
||||||
Disclosable::new(disclosed, self, action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use button_component::Button;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::{Component, ContainerStyle, Flex, Label, ParentElement},
|
elements::{ContainerStyle, Flex, Label, ParentElement, StatefulComponent},
|
||||||
fonts::{self, TextStyle},
|
fonts::{self, TextStyle},
|
||||||
platform::WindowOptions,
|
platform::WindowOptions,
|
||||||
AnyElement, App, Element, Entity, View, ViewContext,
|
AnyElement, App, Element, Entity, View, ViewContext,
|
||||||
|
@ -72,7 +72,7 @@ impl View for TestView {
|
||||||
TextStyle::for_color(Color::blue()),
|
TextStyle::for_color(Color::blue()),
|
||||||
)
|
)
|
||||||
.with_style(ButtonStyle::fill(Color::yellow()))
|
.with_style(ButtonStyle::fill(Color::yellow()))
|
||||||
.c_element(),
|
.stateful_element(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
ToggleableButton::new(self.is_doubling, move |_, v: &mut Self, cx| {
|
ToggleableButton::new(self.is_doubling, move |_, v: &mut Self, cx| {
|
||||||
|
@ -84,7 +84,7 @@ impl View for TestView {
|
||||||
inactive: ButtonStyle::fill(Color::red()),
|
inactive: ButtonStyle::fill(Color::red()),
|
||||||
active: ButtonStyle::fill(Color::green()),
|
active: ButtonStyle::fill(Color::green()),
|
||||||
})
|
})
|
||||||
.c_element(),
|
.stateful_element(),
|
||||||
)
|
)
|
||||||
.expanded()
|
.expanded()
|
||||||
.contained()
|
.contained()
|
||||||
|
@ -114,7 +114,7 @@ mod theme {
|
||||||
// Component creation:
|
// Component creation:
|
||||||
mod toggleable_button {
|
mod toggleable_button {
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::{Component, ContainerStyle, LabelStyle},
|
elements::{ContainerStyle, LabelStyle, StatefulComponent},
|
||||||
scene::MouseClick,
|
scene::MouseClick,
|
||||||
EventContext, View,
|
EventContext, View,
|
||||||
};
|
};
|
||||||
|
@ -156,7 +156,7 @@ mod toggleable_button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View> Component<V> for ToggleableButton<V> {
|
impl<V: View> StatefulComponent<V> for ToggleableButton<V> {
|
||||||
fn render(self, v: &mut V, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
fn render(self, v: &mut V, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||||
let button = if let Some(style) = self.style {
|
let button = if let Some(style) = self.style {
|
||||||
self.button.with_style(*style.style_for(self.active))
|
self.button.with_style(*style.style_for(self.active))
|
||||||
|
@ -171,7 +171,7 @@ mod toggleable_button {
|
||||||
mod button_component {
|
mod button_component {
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::{Component, ContainerStyle, Label, LabelStyle, MouseEventHandler},
|
elements::{ContainerStyle, Label, LabelStyle, MouseEventHandler, StatefulComponent},
|
||||||
platform::MouseButton,
|
platform::MouseButton,
|
||||||
scene::MouseClick,
|
scene::MouseClick,
|
||||||
AnyElement, Element, EventContext, TypeTag, View, ViewContext,
|
AnyElement, Element, EventContext, TypeTag, View, ViewContext,
|
||||||
|
@ -212,7 +212,7 @@ mod button_component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View> Component<V> for Button<V> {
|
impl<V: View> StatefulComponent<V> for Button<V> {
|
||||||
fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
||||||
let click_handler = self.click_handler;
|
let click_handler = self.click_handler;
|
||||||
|
|
||||||
|
|
|
@ -230,19 +230,26 @@ pub trait Element<V: View>: 'static {
|
||||||
MouseEventHandler::for_child::<Tag>(self.into_any(), region_id)
|
MouseEventHandler::for_child::<Tag>(self.into_any(), region_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn component(self) -> ElementAdapter<V>
|
fn stateful_component(self) -> ElementAdapter<V>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
ElementAdapter::new(self.into_any())
|
ElementAdapter::new(self.into_any())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dynamic_component(self) -> DynamicElementAdapter
|
fn component(self) -> DynamicElementAdapter
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
DynamicElementAdapter::new(self.into_any())
|
DynamicElementAdapter::new(self.into_any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn styleable_component(self) -> StylableAdapter<DynamicElementAdapter>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
DynamicElementAdapter::new(self.into_any()).stylable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RenderElement {
|
pub trait RenderElement {
|
||||||
|
|
|
@ -9,35 +9,35 @@ use crate::{
|
||||||
|
|
||||||
use super::Empty;
|
use super::Empty;
|
||||||
|
|
||||||
pub trait GeneralComponent {
|
pub trait Component {
|
||||||
fn render<V: View>(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
fn render<V: View>(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||||
|
|
||||||
fn element<V: View>(self) -> ComponentAdapter<V, Self>
|
fn element<V: View>(self) -> StatefulAdapter<V, Self>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
ComponentAdapter::new(self)
|
StatefulAdapter::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stylable(self) -> GeneralStylableComponentAdapter<Self>
|
fn stylable(self) -> StylableAdapter<Self>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
GeneralStylableComponentAdapter::new(self)
|
StylableAdapter::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GeneralStylableComponentAdapter<C: GeneralComponent> {
|
pub struct StylableAdapter<C: Component> {
|
||||||
component: C,
|
component: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralComponent> GeneralStylableComponentAdapter<C> {
|
impl<C: Component> StylableAdapter<C> {
|
||||||
pub fn new(component: C) -> Self {
|
pub fn new(component: C) -> Self {
|
||||||
Self { component }
|
Self { component }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralComponent> GeneralStyleableComponent for GeneralStylableComponentAdapter<C> {
|
impl<C: Component> StyleableComponent for StylableAdapter<C> {
|
||||||
type Style = ();
|
type Style = ();
|
||||||
|
|
||||||
type Output = C;
|
type Output = C;
|
||||||
|
@ -47,20 +47,20 @@ impl<C: GeneralComponent> GeneralStyleableComponent for GeneralStylableComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GeneralStyleableComponent {
|
pub trait StyleableComponent {
|
||||||
type Style: Clone;
|
type Style: Clone;
|
||||||
type Output: GeneralComponent;
|
type Output: Component;
|
||||||
|
|
||||||
fn with_style(self, style: Self::Style) -> Self::Output;
|
fn with_style(self, style: Self::Style) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralComponent for () {
|
impl Component for () {
|
||||||
fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||||
Empty::new().into_any()
|
Empty::new().into_any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralStyleableComponent for () {
|
impl StyleableComponent for () {
|
||||||
type Style = ();
|
type Style = ();
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
|
@ -69,54 +69,54 @@ impl GeneralStyleableComponent for () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StyleableComponent<V: View> {
|
pub trait StatefulStyleableComponent<V: View> {
|
||||||
type Style: Clone;
|
type Style: Clone;
|
||||||
type Output: Component<V>;
|
type Output: StatefulComponent<V>;
|
||||||
|
|
||||||
fn c_with_style(self, style: Self::Style) -> Self::Output;
|
fn stateful_with_style(self, style: Self::Style) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View, C: GeneralStyleableComponent> StyleableComponent<V> for C {
|
impl<V: View, C: StyleableComponent> StatefulStyleableComponent<V> for C {
|
||||||
type Style = C::Style;
|
type Style = C::Style;
|
||||||
|
|
||||||
type Output = C::Output;
|
type Output = C::Output;
|
||||||
|
|
||||||
fn c_with_style(self, style: Self::Style) -> Self::Output {
|
fn stateful_with_style(self, style: Self::Style) -> Self::Output {
|
||||||
self.with_style(style)
|
self.with_style(style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component<V: View> {
|
pub trait StatefulComponent<V: View> {
|
||||||
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||||
|
|
||||||
fn c_element(self) -> ComponentAdapter<V, Self>
|
fn stateful_element(self) -> StatefulAdapter<V, Self>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
ComponentAdapter::new(self)
|
StatefulAdapter::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn c_styleable(self) -> StylableComponentAdapter<Self, V>
|
fn stateful_styleable(self) -> StatefulStylableAdapter<Self, V>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
StylableComponentAdapter::new(self)
|
StatefulStylableAdapter::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View, C: GeneralComponent> Component<V> for C {
|
impl<V: View, C: Component> StatefulComponent<V> for C {
|
||||||
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
||||||
self.render(v, cx)
|
self.render(v, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StylableComponent -> Component
|
// StylableComponent -> Component
|
||||||
pub struct StylableComponentAdapter<C: Component<V>, V: View> {
|
pub struct StatefulStylableAdapter<C: StatefulComponent<V>, V: View> {
|
||||||
component: C,
|
component: C,
|
||||||
phantom: std::marker::PhantomData<V>,
|
phantom: std::marker::PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
|
impl<C: StatefulComponent<V>, V: View> StatefulStylableAdapter<C, V> {
|
||||||
pub fn new(component: C) -> Self {
|
pub fn new(component: C) -> Self {
|
||||||
Self {
|
Self {
|
||||||
component,
|
component,
|
||||||
|
@ -125,12 +125,14 @@ impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Component<V>, V: View> StyleableComponent<V> for StylableComponentAdapter<C, V> {
|
impl<C: StatefulComponent<V>, V: View> StatefulStyleableComponent<V>
|
||||||
|
for StatefulStylableAdapter<C, V>
|
||||||
|
{
|
||||||
type Style = ();
|
type Style = ();
|
||||||
|
|
||||||
type Output = C;
|
type Output = C;
|
||||||
|
|
||||||
fn c_with_style(self, _: Self::Style) -> Self::Output {
|
fn stateful_with_style(self, _: Self::Style) -> Self::Output {
|
||||||
self.component
|
self.component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +151,7 @@ impl DynamicElementAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralComponent for DynamicElementAdapter {
|
impl Component for DynamicElementAdapter {
|
||||||
fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||||
let element = self
|
let element = self
|
||||||
.element
|
.element
|
||||||
|
@ -174,20 +176,20 @@ impl<V: View> ElementAdapter<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View> Component<V> for ElementAdapter<V> {
|
impl<V: View> StatefulComponent<V> for ElementAdapter<V> {
|
||||||
fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||||
self.element
|
self.element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Component -> Element
|
// Component -> Element
|
||||||
pub struct ComponentAdapter<V: View, E> {
|
pub struct StatefulAdapter<V: View, E> {
|
||||||
component: Option<E>,
|
component: Option<E>,
|
||||||
element: Option<AnyElement<V>>,
|
element: Option<AnyElement<V>>,
|
||||||
phantom: PhantomData<V>,
|
phantom: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, V: View> ComponentAdapter<V, E> {
|
impl<E, V: View> StatefulAdapter<V, E> {
|
||||||
pub fn new(e: E) -> Self {
|
pub fn new(e: E) -> Self {
|
||||||
Self {
|
Self {
|
||||||
component: Some(e),
|
component: Some(e),
|
||||||
|
@ -197,7 +199,7 @@ impl<E, V: View> ComponentAdapter<V, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
|
impl<V: View, C: StatefulComponent<V> + 'static> Element<V> for StatefulAdapter<V, C> {
|
||||||
type LayoutState = ();
|
type LayoutState = ();
|
||||||
|
|
||||||
type PaintState = ();
|
type PaintState = ();
|
||||||
|
|
|
@ -45,6 +45,14 @@ impl ContainerStyle {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn additional_length(&self) -> f32 {
|
||||||
|
self.padding.left
|
||||||
|
+ self.padding.right
|
||||||
|
+ self.border.width * 2.
|
||||||
|
+ self.margin.left
|
||||||
|
+ self.margin.right
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Container<V: View> {
|
pub struct Container<V: View> {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use bitflags::bitflags;
|
||||||
pub use buffer_search::BufferSearchBar;
|
pub use buffer_search::BufferSearchBar;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{Component, GeneralStyleableComponent, TooltipStyle},
|
elements::{Component, StyleableComponent, TooltipStyle},
|
||||||
Action, AnyElement, AppContext, Element, View,
|
Action, AnyElement, AppContext, Element, View,
|
||||||
};
|
};
|
||||||
pub use mode::SearchMode;
|
pub use mode::SearchMode;
|
||||||
|
@ -96,7 +96,7 @@ impl SearchOptions {
|
||||||
.with_contents(Svg::new(self.icon()))
|
.with_contents(Svg::new(self.icon()))
|
||||||
.toggleable(active)
|
.toggleable(active)
|
||||||
.with_style(button_style)
|
.with_style(button_style)
|
||||||
.c_element()
|
.element()
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,147 @@
|
||||||
use gpui::elements::GeneralStyleableComponent;
|
use gpui::{elements::StyleableComponent, Action};
|
||||||
|
|
||||||
use crate::{Interactive, Toggleable};
|
use crate::{Interactive, Toggleable};
|
||||||
|
|
||||||
use self::{action_button::ButtonStyle, svg::SvgStyle, toggle::Toggle};
|
use self::{action_button::ButtonStyle, disclosure::Disclosable, svg::SvgStyle, toggle::Toggle};
|
||||||
|
|
||||||
pub type ToggleIconButtonStyle = Toggleable<Interactive<ButtonStyle<SvgStyle>>>;
|
pub type ToggleIconButtonStyle = Toggleable<Interactive<ButtonStyle<SvgStyle>>>;
|
||||||
|
|
||||||
pub trait ComponentExt<C: GeneralStyleableComponent> {
|
pub trait ComponentExt<C: StyleableComponent> {
|
||||||
fn toggleable(self, active: bool) -> Toggle<C, ()>;
|
fn toggleable(self, active: bool) -> Toggle<C, ()>;
|
||||||
|
fn disclosable(
|
||||||
|
self,
|
||||||
|
disclosed: Option<bool>,
|
||||||
|
action: Box<dyn Action>,
|
||||||
|
id: usize,
|
||||||
|
) -> Disclosable<C, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> ComponentExt<C> for C {
|
impl<C: StyleableComponent> ComponentExt<C> for C {
|
||||||
fn toggleable(self, active: bool) -> Toggle<C, ()> {
|
fn toggleable(self, active: bool) -> Toggle<C, ()> {
|
||||||
Toggle::new(self, active)
|
Toggle::new(self, active)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Some(True) => disclosed => content is visible
|
||||||
|
/// Some(false) => closed => content is hidden
|
||||||
|
/// None => No disclosure button, but reserve spacing
|
||||||
|
fn disclosable(
|
||||||
|
self,
|
||||||
|
disclosed: Option<bool>,
|
||||||
|
action: Box<dyn Action>,
|
||||||
|
id: usize,
|
||||||
|
) -> Disclosable<C, ()> {
|
||||||
|
Disclosable::new(disclosed, self, action, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod disclosure {
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
elements::{Component, Empty, Flex, ParentElement, StyleableComponent},
|
||||||
|
Action, Element,
|
||||||
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
use super::{action_button::ActionButton, svg::Svg, ComponentExt, ToggleIconButtonStyle};
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Deserialize, JsonSchema)]
|
||||||
|
pub struct DisclosureStyle<S> {
|
||||||
|
pub button: ToggleIconButtonStyle,
|
||||||
|
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>,
|
||||||
|
id: usize,
|
||||||
|
) -> Disclosable<C, ()> {
|
||||||
|
Disclosable {
|
||||||
|
disclosed,
|
||||||
|
content,
|
||||||
|
action,
|
||||||
|
id,
|
||||||
|
style: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: StyleableComponent> StyleableComponent 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: StyleableComponent> Component for Disclosable<C, DisclosureStyle<C::Style>> {
|
||||||
|
fn render<V: gpui::View>(
|
||||||
|
self,
|
||||||
|
v: &mut V,
|
||||||
|
cx: &mut gpui::ViewContext<V>,
|
||||||
|
) -> gpui::AnyElement<V> {
|
||||||
|
Flex::row()
|
||||||
|
.with_child(if let Some(disclosed) = self.disclosed {
|
||||||
|
ActionButton::new_dynamic(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"
|
||||||
|
}))
|
||||||
|
.toggleable(disclosed)
|
||||||
|
.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(Empty::new().constrained().with_width(self.style.spacing))
|
||||||
|
.with_child(
|
||||||
|
self.content
|
||||||
|
.with_style(self.style.content)
|
||||||
|
.render(v, cx)
|
||||||
|
.flex(1., true),
|
||||||
|
)
|
||||||
|
.align_children_center()
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod toggle {
|
pub mod toggle {
|
||||||
use gpui::elements::{GeneralComponent, GeneralStyleableComponent};
|
use gpui::elements::{Component, StyleableComponent};
|
||||||
|
|
||||||
use crate::Toggleable;
|
use crate::Toggleable;
|
||||||
|
|
||||||
|
@ -27,7 +151,7 @@ pub mod toggle {
|
||||||
component: C,
|
component: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> Toggle<C, ()> {
|
impl<C: StyleableComponent> Toggle<C, ()> {
|
||||||
pub fn new(component: C, active: bool) -> Self {
|
pub fn new(component: C, active: bool) -> Self {
|
||||||
Toggle {
|
Toggle {
|
||||||
active,
|
active,
|
||||||
|
@ -37,7 +161,7 @@ pub mod toggle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> GeneralStyleableComponent for Toggle<C, ()> {
|
impl<C: StyleableComponent> StyleableComponent for Toggle<C, ()> {
|
||||||
type Style = Toggleable<C::Style>;
|
type Style = Toggleable<C::Style>;
|
||||||
|
|
||||||
type Output = Toggle<C, Self::Style>;
|
type Output = Toggle<C, Self::Style>;
|
||||||
|
@ -51,7 +175,7 @@ pub mod toggle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> GeneralComponent for Toggle<C, Toggleable<C::Style>> {
|
impl<C: StyleableComponent> Component for Toggle<C, Toggleable<C::Style>> {
|
||||||
fn render<V: gpui::View>(
|
fn render<V: gpui::View>(
|
||||||
self,
|
self,
|
||||||
v: &mut V,
|
v: &mut V,
|
||||||
|
@ -69,8 +193,7 @@ pub mod action_button {
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::{
|
elements::{
|
||||||
ContainerStyle, GeneralComponent, GeneralStyleableComponent, MouseEventHandler,
|
Component, ContainerStyle, MouseEventHandler, StyleableComponent, TooltipStyle,
|
||||||
TooltipStyle,
|
|
||||||
},
|
},
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
Action, Element, TypeTag, View,
|
Action, Element, TypeTag, View,
|
||||||
|
@ -80,24 +203,28 @@ pub mod action_button {
|
||||||
|
|
||||||
use crate::Interactive;
|
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 ActionButton<C, S> {
|
pub struct ActionButton<C, S> {
|
||||||
action: Box<dyn Action>,
|
action: Box<dyn Action>,
|
||||||
tooltip: Option<(Cow<'static, str>, TooltipStyle)>,
|
tooltip: Option<(Cow<'static, str>, TooltipStyle)>,
|
||||||
tag: TypeTag,
|
tag: TypeTag,
|
||||||
|
id: usize,
|
||||||
contents: C,
|
contents: C,
|
||||||
style: Interactive<S>,
|
style: Interactive<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
|
||||||
pub struct ButtonStyle<C> {
|
|
||||||
#[serde(flatten)]
|
|
||||||
container: ContainerStyle,
|
|
||||||
button_width: Option<f32>,
|
|
||||||
button_height: Option<f32>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
contents: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActionButton<(), ()> {
|
impl ActionButton<(), ()> {
|
||||||
pub fn new_dynamic(action: Box<dyn Action>) -> Self {
|
pub fn new_dynamic(action: Box<dyn Action>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -105,6 +232,7 @@ pub mod action_button {
|
||||||
tag: action.type_tag(),
|
tag: action.type_tag(),
|
||||||
style: Interactive::new_blank(),
|
style: Interactive::new_blank(),
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
|
id: 0,
|
||||||
action,
|
action,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,21 +250,24 @@ pub mod action_button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_contents<C: GeneralStyleableComponent>(
|
pub fn with_id(mut self, id: usize) -> Self {
|
||||||
self,
|
self.id = id;
|
||||||
contents: C,
|
self
|
||||||
) -> ActionButton<C, ()> {
|
}
|
||||||
|
|
||||||
|
pub fn with_contents<C: StyleableComponent>(self, contents: C) -> ActionButton<C, ()> {
|
||||||
ActionButton {
|
ActionButton {
|
||||||
action: self.action,
|
action: self.action,
|
||||||
tag: self.tag,
|
tag: self.tag,
|
||||||
style: self.style,
|
style: self.style,
|
||||||
tooltip: self.tooltip,
|
tooltip: self.tooltip,
|
||||||
|
id: self.id,
|
||||||
contents,
|
contents,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> GeneralStyleableComponent for ActionButton<C, ()> {
|
impl<C: StyleableComponent> StyleableComponent for ActionButton<C, ()> {
|
||||||
type Style = Interactive<ButtonStyle<C::Style>>;
|
type Style = Interactive<ButtonStyle<C::Style>>;
|
||||||
type Output = ActionButton<C, ButtonStyle<C::Style>>;
|
type Output = ActionButton<C, ButtonStyle<C::Style>>;
|
||||||
|
|
||||||
|
@ -146,15 +277,15 @@ pub mod action_button {
|
||||||
tag: self.tag,
|
tag: self.tag,
|
||||||
contents: self.contents,
|
contents: self.contents,
|
||||||
tooltip: self.tooltip,
|
tooltip: self.tooltip,
|
||||||
|
id: self.id,
|
||||||
style,
|
style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: GeneralStyleableComponent> GeneralComponent for ActionButton<C, ButtonStyle<C::Style>> {
|
impl<C: StyleableComponent> Component for ActionButton<C, ButtonStyle<C::Style>> {
|
||||||
fn render<V: View>(self, v: &mut V, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
fn render<V: View>(self, v: &mut V, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
||||||
let mut button = MouseEventHandler::new_dynamic(self.tag, 0, cx, |state, cx| {
|
let mut button = MouseEventHandler::new_dynamic(self.tag, self.id, cx, |state, cx| {
|
||||||
let style = self.style.style_for(state);
|
let style = self.style.style_for(state);
|
||||||
let mut contents = self
|
let mut contents = self
|
||||||
.contents
|
.contents
|
||||||
|
@ -177,8 +308,13 @@ pub mod action_button {
|
||||||
.on_click(MouseButton::Left, {
|
.on_click(MouseButton::Left, {
|
||||||
let action = self.action.boxed_clone();
|
let action = self.action.boxed_clone();
|
||||||
move |_, _, cx| {
|
move |_, _, cx| {
|
||||||
cx.window()
|
let window = cx.window();
|
||||||
.dispatch_action(cx.view_id(), action.as_ref(), cx);
|
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)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
@ -199,7 +335,7 @@ pub mod svg {
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::{GeneralComponent, GeneralStyleableComponent},
|
elements::{Component, Empty, StyleableComponent},
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
@ -222,6 +358,7 @@ pub mod svg {
|
||||||
pub enum IconSize {
|
pub enum IconSize {
|
||||||
IconSize { icon_size: f32 },
|
IconSize { icon_size: f32 },
|
||||||
Dimensions { width: f32, height: f32 },
|
Dimensions { width: f32, height: f32 },
|
||||||
|
IconDimensions { icon_width: f32, icon_height: f32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -245,6 +382,14 @@ pub mod svg {
|
||||||
icon_height: height,
|
icon_height: height,
|
||||||
color,
|
color,
|
||||||
},
|
},
|
||||||
|
IconSize::IconDimensions {
|
||||||
|
icon_width,
|
||||||
|
icon_height,
|
||||||
|
} => SvgStyle {
|
||||||
|
icon_width,
|
||||||
|
icon_height,
|
||||||
|
color,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -252,20 +397,27 @@ pub mod svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Svg<S> {
|
pub struct Svg<S> {
|
||||||
path: Cow<'static, str>,
|
path: Option<Cow<'static, str>>,
|
||||||
style: S,
|
style: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Svg<()> {
|
impl Svg<()> {
|
||||||
pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
|
pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
path: path.into(),
|
path: Some(path.into()),
|
||||||
|
style: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optional(path: Option<impl Into<Cow<'static, str>>>) -> Self {
|
||||||
|
Self {
|
||||||
|
path: path.map(Into::into),
|
||||||
style: (),
|
style: (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralStyleableComponent for Svg<()> {
|
impl StyleableComponent for Svg<()> {
|
||||||
type Style = SvgStyle;
|
type Style = SvgStyle;
|
||||||
|
|
||||||
type Output = Svg<SvgStyle>;
|
type Output = Svg<SvgStyle>;
|
||||||
|
@ -278,18 +430,23 @@ pub mod svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralComponent for Svg<SvgStyle> {
|
impl Component for Svg<SvgStyle> {
|
||||||
fn render<V: gpui::View>(
|
fn render<V: gpui::View>(
|
||||||
self,
|
self,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
_: &mut gpui::ViewContext<V>,
|
_: &mut gpui::ViewContext<V>,
|
||||||
) -> gpui::AnyElement<V> {
|
) -> gpui::AnyElement<V> {
|
||||||
gpui::elements::Svg::new(self.path)
|
if let Some(path) = self.path {
|
||||||
.with_color(self.style.color)
|
gpui::elements::Svg::new(path)
|
||||||
.constrained()
|
.with_color(self.style.color)
|
||||||
.with_width(self.style.icon_width)
|
.constrained()
|
||||||
.with_height(self.style.icon_height)
|
} else {
|
||||||
.into_any()
|
Empty::new().constrained()
|
||||||
|
}
|
||||||
|
.constrained()
|
||||||
|
.with_width(self.style.icon_width)
|
||||||
|
.with_height(self.style.icon_height)
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,7 +455,7 @@ pub mod label {
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::{GeneralComponent, GeneralStyleableComponent, LabelStyle},
|
elements::{Component, LabelStyle, StyleableComponent},
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -316,7 +473,7 @@ pub mod label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralStyleableComponent for Label<()> {
|
impl StyleableComponent for Label<()> {
|
||||||
type Style = LabelStyle;
|
type Style = LabelStyle;
|
||||||
|
|
||||||
type Output = Label<LabelStyle>;
|
type Output = Label<LabelStyle>;
|
||||||
|
@ -329,7 +486,7 @@ pub mod label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralComponent for Label<LabelStyle> {
|
impl Component for Label<LabelStyle> {
|
||||||
fn render<V: gpui::View>(
|
fn render<V: gpui::View>(
|
||||||
self,
|
self,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
|
|
|
@ -3,7 +3,7 @@ mod theme_registry;
|
||||||
mod theme_settings;
|
mod theme_settings;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
use components::ToggleIconButtonStyle;
|
use components::{disclosure::DisclosureStyle, ToggleIconButtonStyle};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
|
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
|
||||||
|
@ -14,7 +14,7 @@ use schemars::JsonSchema;
|
||||||
use serde::{de::DeserializeOwned, Deserialize};
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, ops::Deref, sync::Arc};
|
||||||
use ui::{CheckboxStyle, CopilotCTAButton, IconStyle, ModalStyle};
|
use ui::{CheckboxStyle, CopilotCTAButton, IconStyle, ModalStyle};
|
||||||
|
|
||||||
pub use theme_registry::*;
|
pub use theme_registry::*;
|
||||||
|
@ -221,6 +221,7 @@ pub struct CopilotAuthAuthorized {
|
||||||
pub struct CollabPanel {
|
pub struct CollabPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
pub disclosure: DisclosureStyle<()>,
|
||||||
pub list_empty_state: Toggleable<Interactive<ContainedText>>,
|
pub list_empty_state: Toggleable<Interactive<ContainedText>>,
|
||||||
pub list_empty_icon: Icon,
|
pub list_empty_icon: Icon,
|
||||||
pub list_empty_label_container: ContainerStyle,
|
pub list_empty_label_container: ContainerStyle,
|
||||||
|
@ -890,6 +891,14 @@ pub struct Interactive<T> {
|
||||||
pub disabled: Option<T>,
|
pub disabled: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Interactive<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Interactive<()> {
|
impl Interactive<()> {
|
||||||
pub fn new_blank() -> Self {
|
pub fn new_blank() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -907,6 +916,14 @@ pub struct Toggleable<T> {
|
||||||
inactive: T,
|
inactive: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Toggleable<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Toggleable<()> {
|
impl Toggleable<()> {
|
||||||
pub fn new_blank() -> Self {
|
pub fn new_blank() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -152,6 +152,10 @@ export default function contacts_panel(): any {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...collab_modals(),
|
...collab_modals(),
|
||||||
|
disclosure: {
|
||||||
|
button: toggleable_icon_button(theme, {}),
|
||||||
|
spacing: 4,
|
||||||
|
},
|
||||||
log_in_button: interactive({
|
log_in_button: interactive({
|
||||||
base: {
|
base: {
|
||||||
background: background(theme.middle),
|
background: background(theme.middle),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue