Detailed changes
@@ -1095,6 +1095,23 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "breadcrumbs2"
+version = "0.1.0"
+dependencies = [
+ "collections",
+ "editor2",
+ "gpui2",
+ "itertools 0.10.5",
+ "language2",
+ "project2",
+ "search2",
+ "settings2",
+ "theme2",
+ "ui2",
+ "workspace2",
+]
+
[[package]]
name = "bromberg_sl2"
version = "0.6.0"
@@ -1688,7 +1705,7 @@ dependencies = [
[[package]]
name = "collab"
-version = "0.29.0"
+version = "0.29.1"
dependencies = [
"anyhow",
"async-trait",
@@ -2126,6 +2143,25 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "copilot_button2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "copilot2",
+ "editor2",
+ "fs2",
+ "futures 0.3.28",
+ "gpui2",
+ "language2",
+ "settings2",
+ "smol",
+ "theme2",
+ "util",
+ "workspace2",
+ "zed_actions2",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.3"
@@ -4774,6 +4810,24 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "language_selector2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "editor2",
+ "fuzzy2",
+ "gpui2",
+ "language2",
+ "picker2",
+ "project2",
+ "settings2",
+ "theme2",
+ "ui2",
+ "util",
+ "workspace2",
+]
+
[[package]]
name = "language_tools"
version = "0.1.0"
@@ -11726,6 +11780,7 @@ dependencies = [
"audio2",
"auto_update2",
"backtrace",
+ "breadcrumbs2",
"call2",
"channel2",
"chrono",
@@ -11735,6 +11790,7 @@ dependencies = [
"collections",
"command_palette2",
"copilot2",
+ "copilot_button2",
"ctor",
"db2",
"diagnostics2",
@@ -11754,6 +11810,7 @@ dependencies = [
"isahc",
"journal2",
"language2",
+ "language_selector2",
"lazy_static",
"libc",
"log",
@@ -9,6 +9,7 @@ members = [
"crates/auto_update",
"crates/auto_update2",
"crates/breadcrumbs",
+ "crates/breadcrumbs2",
"crates/call",
"crates/call2",
"crates/channel",
@@ -60,6 +61,7 @@ members = [
"crates/language",
"crates/language2",
"crates/language_selector",
+ "crates/language_selector2",
"crates/language_tools",
"crates/live_kit_client",
"crates/live_kit_server",
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
@@ -102,7 +102,7 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppCo
})
.detach();
- if let Some(version) = *ZED_APP_VERSION {
+ if let Some(version) = ZED_APP_VERSION.or_else(|| cx.app_metadata().app_version) {
let auto_updater = cx.build_model(|cx| {
let updater = AutoUpdater::new(version, http_client, server_url);
@@ -0,0 +1,28 @@
+[package]
+name = "breadcrumbs2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/breadcrumbs.rs"
+doctest = false
+
+[dependencies]
+collections = { path = "../collections" }
+editor = { package = "editor2", path = "../editor2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+ui = { package = "ui2", path = "../ui2" }
+language = { package = "language2", path = "../language2" }
+project = { package = "project2", path = "../project2" }
+search = { package = "search2", path = "../search2" }
+settings = { package = "settings2", path = "../settings2" }
+theme = { package = "theme2", path = "../theme2" }
+workspace = { package = "workspace2", path = "../workspace2" }
+# outline = { path = "../outline" }
+itertools = "0.10"
+
+[dev-dependencies]
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
@@ -0,0 +1,204 @@
+use gpui::{
+ Component, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
+ ViewContext, WeakView,
+};
+use itertools::Itertools;
+use theme::ActiveTheme;
+use ui::{ButtonCommon, ButtonLike, ButtonStyle, Clickable, Disableable, Label};
+use workspace::{
+ item::{ItemEvent, ItemHandle},
+ ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
+};
+
+pub enum Event {
+ UpdateLocation,
+}
+
+pub struct Breadcrumbs {
+ pane_focused: bool,
+ active_item: Option<Box<dyn ItemHandle>>,
+ subscription: Option<Subscription>,
+ _workspace: WeakView<Workspace>,
+}
+
+impl Breadcrumbs {
+ pub fn new(workspace: &Workspace) -> Self {
+ Self {
+ pane_focused: false,
+ active_item: Default::default(),
+ subscription: Default::default(),
+ _workspace: workspace.weak_handle(),
+ }
+ }
+}
+
+impl EventEmitter<Event> for Breadcrumbs {}
+impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
+
+impl Render for Breadcrumbs {
+ type Element = Component<ButtonLike>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let button = ButtonLike::new("breadcrumbs")
+ .style(ButtonStyle::Transparent)
+ .disabled(true);
+
+ let active_item = match &self.active_item {
+ Some(active_item) => active_item,
+ None => return button.into_element(),
+ };
+ let not_editor = active_item.downcast::<editor::Editor>().is_none();
+
+ let breadcrumbs = match active_item.breadcrumbs(cx.theme(), cx) {
+ Some(breadcrumbs) => breadcrumbs,
+ None => return button.into_element(),
+ }
+ .into_iter()
+ .map(|breadcrumb| {
+ StyledText::new(breadcrumb.text)
+ .with_highlights(&cx.text_style(), breadcrumb.highlights.unwrap_or_default())
+ .into_any()
+ });
+
+ let button = button.children(Itertools::intersperse_with(breadcrumbs, || {
+ Label::new(" › ").into_any_element()
+ }));
+
+ if not_editor || !self.pane_focused {
+ return button.into_element();
+ }
+
+ // let this = cx.view().downgrade();
+ button
+ .style(ButtonStyle::Filled)
+ .disabled(false)
+ .on_click(move |_, _cx| {
+ todo!("outline::toggle");
+ // this.update(cx, |this, cx| {
+ // if let Some(workspace) = this.workspace.upgrade() {
+ // workspace.update(cx, |_workspace, _cx| {
+ // outline::toggle(workspace, &Default::default(), cx)
+ // })
+ // }
+ // })
+ // .ok();
+ })
+ .into_element()
+ }
+}
+
+// impl View for Breadcrumbs {
+// fn ui_name() -> &'static str {
+// "Breadcrumbs"
+// }
+
+// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+// let active_item = match &self.active_item {
+// Some(active_item) => active_item,
+// None => return Empty::new().into_any(),
+// };
+// let not_editor = active_item.downcast::<editor::Editor>().is_none();
+
+// let theme = theme::current(cx).clone();
+// let style = &theme.workspace.toolbar.breadcrumbs;
+
+// let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
+// Some(breadcrumbs) => breadcrumbs,
+// None => return Empty::new().into_any(),
+// }
+// .into_iter()
+// .map(|breadcrumb| {
+// Text::new(
+// breadcrumb.text,
+// theme.workspace.toolbar.breadcrumbs.default.text.clone(),
+// )
+// .with_highlights(breadcrumb.highlights.unwrap_or_default())
+// .into_any()
+// });
+
+// let crumbs = Flex::row()
+// .with_children(Itertools::intersperse_with(breadcrumbs, || {
+// Label::new(" › ", style.default.text.clone()).into_any()
+// }))
+// .constrained()
+// .with_height(theme.workspace.toolbar.breadcrumb_height)
+// .contained();
+
+// if not_editor || !self.pane_focused {
+// return crumbs
+// .with_style(style.default.container)
+// .aligned()
+// .left()
+// .into_any();
+// }
+
+// MouseEventHandler::new::<Breadcrumbs, _>(0, cx, |state, _| {
+// let style = style.style_for(state);
+// crumbs.with_style(style.container)
+// })
+// .on_click(MouseButton::Left, |_, this, cx| {
+// if let Some(workspace) = this.workspace.upgrade(cx) {
+// workspace.update(cx, |workspace, cx| {
+// outline::toggle(workspace, &Default::default(), cx)
+// })
+// }
+// })
+// .with_tooltip::<Breadcrumbs>(
+// 0,
+// "Show symbol outline".to_owned(),
+// Some(Box::new(outline::Toggle)),
+// theme.tooltip.clone(),
+// cx,
+// )
+// .aligned()
+// .left()
+// .into_any()
+// }
+// }
+
+impl ToolbarItemView for Breadcrumbs {
+ fn set_active_pane_item(
+ &mut self,
+ active_pane_item: Option<&dyn ItemHandle>,
+ cx: &mut ViewContext<Self>,
+ ) -> ToolbarItemLocation {
+ cx.notify();
+ self.active_item = None;
+ if let Some(item) = active_pane_item {
+ let this = cx.view().downgrade();
+ self.subscription = Some(item.subscribe_to_item_events(
+ cx,
+ Box::new(move |event, cx| {
+ if let ItemEvent::UpdateBreadcrumbs = event {
+ this.update(cx, |_, cx| {
+ cx.emit(Event::UpdateLocation);
+ cx.notify();
+ })
+ .ok();
+ }
+ }),
+ ));
+ self.active_item = Some(item.boxed_clone());
+ item.breadcrumb_location(cx)
+ } else {
+ ToolbarItemLocation::Hidden
+ }
+ }
+
+ // fn location_for_event(
+ // &self,
+ // _: &Event,
+ // current_location: ToolbarItemLocation,
+ // cx: &AppContext,
+ // ) -> ToolbarItemLocation {
+ // if let Some(active_item) = self.active_item.as_ref() {
+ // active_item.breadcrumb_location(cx)
+ // } else {
+ // current_location
+ // }
+ // }
+
+ fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
+ self.pane_focused = pane_focused;
+ }
+}
@@ -346,7 +346,7 @@ impl<T: Entity> Drop for PendingEntitySubscription<T> {
}
}
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct TelemetrySettings {
pub diagnostics: bool,
pub metrics: bool,
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
-version = "0.29.0"
+version = "0.29.1"
publish = false
[[bin]]
@@ -1220,6 +1220,13 @@ impl Database {
self.check_user_is_channel_admin(&new_parent, admin_id, &*tx)
.await?;
+ if new_parent
+ .ancestors_including_self()
+ .any(|id| id == channel.id)
+ {
+ Err(anyhow!("cannot move a channel into one of its descendants"))?;
+ }
+
new_parent_path = new_parent.path();
new_parent_channel = Some(new_parent);
} else {
@@ -450,6 +450,20 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
(livestreaming_id, &[projects_id]),
],
);
+
+ // Can't move a channel into its ancestor
+ db.move_channel(projects_id, Some(livestreaming_id), user_id)
+ .await
+ .unwrap_err();
+ let result = db.get_channels_for_user(user_id).await.unwrap();
+ assert_channel_tree(
+ result.channels,
+ &[
+ (zed_id, &[]),
+ (projects_id, &[]),
+ (livestreaming_id, &[projects_id]),
+ ],
+ );
}
test_both_dbs!(
@@ -1220,6 +1220,13 @@ impl Database {
self.check_user_is_channel_admin(&new_parent, admin_id, &*tx)
.await?;
+ if new_parent
+ .ancestors_including_self()
+ .any(|id| id == channel.id)
+ {
+ Err(anyhow!("cannot move a channel into one of its descendants"))?;
+ }
+
new_parent_path = new_parent.path();
new_parent_channel = Some(new_parent);
} else {
@@ -420,8 +420,6 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
.await
.unwrap();
- // Dag is: zed - projects - livestreaming
-
// Move to same parent should be a no-op
assert!(db
.move_channel(projects_id, Some(zed_id), user_id)
@@ -450,6 +448,20 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
(livestreaming_id, &[projects_id]),
],
);
+
+ // Can't move a channel into its ancestor
+ db.move_channel(projects_id, Some(livestreaming_id), user_id)
+ .await
+ .unwrap_err();
+ let result = db.get_channels_for_user(user_id).await.unwrap();
+ assert_channel_tree(
+ result.channels,
+ &[
+ (zed_id, &[]),
+ (projects_id, &[]),
+ (livestreaming_id, &[projects_id]),
+ ],
+ );
}
test_both_dbs!(
@@ -1,5 +1,5 @@
#![allow(unused)]
-// mod channel_modal;
+mod channel_modal;
mod contact_finder;
// use crate::{
@@ -192,6 +192,8 @@ use workspace::{
use crate::{face_pile::FacePile, CollaborationPanelSettings};
+use self::channel_modal::ChannelModal;
+
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &ToggleFocus, cx| {
@@ -2058,13 +2060,11 @@ impl CollabPanel {
}
fn invite_members(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
- todo!();
- // self.show_channel_modal(channel_id, channel_modal::Mode::InviteMembers, cx);
+ self.show_channel_modal(channel_id, channel_modal::Mode::InviteMembers, cx);
}
fn manage_members(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
- todo!();
- // self.show_channel_modal(channel_id, channel_modal::Mode::ManageMembers, cx);
+ self.show_channel_modal(channel_id, channel_modal::Mode::ManageMembers, cx);
}
fn remove_selected_channel(&mut self, _: &Remove, cx: &mut ViewContext<Self>) {
@@ -2156,38 +2156,36 @@ impl CollabPanel {
})
}
- // fn show_channel_modal(
- // &mut self,
- // channel_id: ChannelId,
- // mode: channel_modal::Mode,
- // cx: &mut ViewContext<Self>,
- // ) {
- // let workspace = self.workspace.clone();
- // let user_store = self.user_store.clone();
- // let channel_store = self.channel_store.clone();
- // let members = self.channel_store.update(cx, |channel_store, cx| {
- // channel_store.get_channel_member_details(channel_id, cx)
- // });
-
- // cx.spawn(|_, mut cx| async move {
- // let members = members.await?;
- // workspace.update(&mut cx, |workspace, cx| {
- // workspace.toggle_modal(cx, |_, cx| {
- // cx.add_view(|cx| {
- // ChannelModal::new(
- // user_store.clone(),
- // channel_store.clone(),
- // channel_id,
- // mode,
- // members,
- // cx,
- // )
- // })
- // });
- // })
- // })
- // .detach();
- // }
+ fn show_channel_modal(
+ &mut self,
+ channel_id: ChannelId,
+ mode: channel_modal::Mode,
+ cx: &mut ViewContext<Self>,
+ ) {
+ let workspace = self.workspace.clone();
+ let user_store = self.user_store.clone();
+ let channel_store = self.channel_store.clone();
+ let members = self.channel_store.update(cx, |channel_store, cx| {
+ channel_store.get_channel_member_details(channel_id, cx)
+ });
+
+ cx.spawn(|_, mut cx| async move {
+ let members = members.await?;
+ workspace.update(&mut cx, |workspace, cx| {
+ workspace.toggle_modal(cx, |cx| {
+ ChannelModal::new(
+ user_store.clone(),
+ channel_store.clone(),
+ channel_id,
+ mode,
+ members,
+ cx,
+ )
+ });
+ })
+ })
+ .detach();
+ }
// fn remove_selected_channel(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) {
// self.remove_channel(action.channel_id, cx)
@@ -3,58 +3,54 @@ use client::{
proto::{self, ChannelRole, ChannelVisibility},
User, UserId, UserStore,
};
-use context_menu::{ContextMenu, ContextMenuItem};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
- actions,
- elements::*,
- platform::{CursorStyle, MouseButton},
- AppContext, ClipboardItem, Entity, ModelHandle, MouseState, Task, View, ViewContext,
- ViewHandle,
+ actions, div, AppContext, ClipboardItem, DismissEvent, Div, Entity, EventEmitter,
+ FocusableView, Model, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext,
+ WeakView,
};
-use picker::{Picker, PickerDelegate, PickerEvent};
+use picker::{Picker, PickerDelegate};
use std::sync::Arc;
+use ui::v_stack;
use util::TryFutureExt;
-use workspace::Modal;
actions!(
- channel_modal,
- [
- SelectNextControl,
- ToggleMode,
- ToggleMemberAdmin,
- RemoveMember
- ]
+ SelectNextControl,
+ ToggleMode,
+ ToggleMemberAdmin,
+ RemoveMember
);
-pub fn init(cx: &mut AppContext) {
- Picker::<ChannelModalDelegate>::init(cx);
- cx.add_action(ChannelModal::toggle_mode);
- cx.add_action(ChannelModal::toggle_member_admin);
- cx.add_action(ChannelModal::remove_member);
- cx.add_action(ChannelModal::dismiss);
-}
+// pub fn init(cx: &mut AppContext) {
+// Picker::<ChannelModalDelegate>::init(cx);
+// cx.add_action(ChannelModal::toggle_mode);
+// cx.add_action(ChannelModal::toggle_member_admin);
+// cx.add_action(ChannelModal::remove_member);
+// cx.add_action(ChannelModal::dismiss);
+// }
pub struct ChannelModal {
- picker: ViewHandle<Picker<ChannelModalDelegate>>,
- channel_store: ModelHandle<ChannelStore>,
+ picker: View<Picker<ChannelModalDelegate>>,
+ channel_store: Model<ChannelStore>,
channel_id: ChannelId,
has_focus: bool,
}
impl ChannelModal {
pub fn new(
- user_store: ModelHandle<UserStore>,
- channel_store: ModelHandle<ChannelStore>,
+ user_store: Model<UserStore>,
+ channel_store: Model<ChannelStore>,
channel_id: ChannelId,
mode: Mode,
members: Vec<ChannelMembership>,
cx: &mut ViewContext<Self>,
) -> Self {
cx.observe(&channel_store, |_, _, cx| cx.notify()).detach();
- let picker = cx.add_view(|cx| {
+ let channel_modal = cx.view().downgrade();
+ let picker = cx.build_view(|cx| {
Picker::new(
ChannelModalDelegate {
+ channel_modal,
matching_users: Vec::new(),
matching_member_indices: Vec::new(),
selected_index: 0,
@@ -64,20 +60,17 @@ impl ChannelModal {
match_candidates: Vec::new(),
members,
mode,
- context_menu: cx.add_view(|cx| {
- let mut menu = ContextMenu::new(cx.view_id(), cx);
- menu.set_position_mode(OverlayPositionMode::Local);
- menu
- }),
+ // context_menu: cx.add_view(|cx| {
+ // let mut menu = ContextMenu::new(cx.view_id(), cx);
+ // menu.set_position_mode(OverlayPositionMode::Local);
+ // menu
+ // }),
},
cx,
)
- .with_theme(|theme| theme.collab_panel.tabbed_modal.picker.clone())
});
- cx.subscribe(&picker, |_, _, e, cx| cx.emit(*e)).detach();
-
- let has_focus = picker.read(cx).has_focus();
+ let has_focus = picker.focus_handle(cx).contains_focused(cx);
Self {
picker,
@@ -88,7 +81,7 @@ impl ChannelModal {
}
fn toggle_mode(&mut self, _: &ToggleMode, cx: &mut ViewContext<Self>) {
- let mode = match self.picker.read(cx).delegate().mode {
+ let mode = match self.picker.read(cx).delegate.mode {
Mode::ManageMembers => Mode::InviteMembers,
Mode::InviteMembers => Mode::ManageMembers,
};
@@ -103,20 +96,20 @@ impl ChannelModal {
let mut members = channel_store
.update(&mut cx, |channel_store, cx| {
channel_store.get_channel_member_details(channel_id, cx)
- })
+ })?
.await?;
members.sort_by(|a, b| a.sort_key().cmp(&b.sort_key()));
this.update(&mut cx, |this, cx| {
this.picker
- .update(cx, |picker, _| picker.delegate_mut().members = members);
+ .update(cx, |picker, _| picker.delegate.members = members);
})?;
}
this.update(&mut cx, |this, cx| {
this.picker.update(cx, |picker, cx| {
- let delegate = picker.delegate_mut();
+ let delegate = &mut picker.delegate;
delegate.mode = mode;
delegate.selected_index = 0;
picker.set_query("", cx);
@@ -131,203 +124,194 @@ impl ChannelModal {
fn toggle_member_admin(&mut self, _: &ToggleMemberAdmin, cx: &mut ViewContext<Self>) {
self.picker.update(cx, |picker, cx| {
- picker.delegate_mut().toggle_selected_member_admin(cx);
+ picker.delegate.toggle_selected_member_admin(cx);
})
}
fn remove_member(&mut self, _: &RemoveMember, cx: &mut ViewContext<Self>) {
self.picker.update(cx, |picker, cx| {
- picker.delegate_mut().remove_selected_member(cx);
+ picker.delegate.remove_selected_member(cx);
});
}
fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
- cx.emit(PickerEvent::Dismiss);
+ cx.emit(DismissEvent);
}
}
-impl Entity for ChannelModal {
- type Event = PickerEvent;
-}
-
-impl View for ChannelModal {
- fn ui_name() -> &'static str {
- "ChannelModal"
- }
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
- let theme = &theme::current(cx).collab_panel.tabbed_modal;
-
- let mode = self.picker.read(cx).delegate().mode;
- let Some(channel) = self.channel_store.read(cx).channel_for_id(self.channel_id) else {
- return Empty::new().into_any();
- };
-
- enum InviteMembers {}
- enum ManageMembers {}
-
- fn render_mode_button<T: 'static>(
- mode: Mode,
- text: &'static str,
- current_mode: Mode,
- theme: &theme::TabbedModal,
- cx: &mut ViewContext<ChannelModal>,
- ) -> AnyElement<ChannelModal> {
- let active = mode == current_mode;
- MouseEventHandler::new::<T, _>(0, cx, move |state, _| {
- let contained_text = theme.tab_button.style_for(active, state);
- Label::new(text, contained_text.text.clone())
- .contained()
- .with_style(contained_text.container.clone())
- })
- .on_click(MouseButton::Left, move |_, this, cx| {
- if !active {
- this.set_mode(mode, cx);
- }
- })
- .with_cursor_style(CursorStyle::PointingHand)
- .into_any()
- }
-
- fn render_visibility(
- channel_id: ChannelId,
- visibility: ChannelVisibility,
- theme: &theme::TabbedModal,
- cx: &mut ViewContext<ChannelModal>,
- ) -> AnyElement<ChannelModal> {
- enum TogglePublic {}
-
- if visibility == ChannelVisibility::Members {
- return Flex::row()
- .with_child(
- MouseEventHandler::new::<TogglePublic, _>(0, cx, move |state, _| {
- let style = theme.visibility_toggle.style_for(state);
- Label::new(format!("{}", "Public access: OFF"), style.text.clone())
- .contained()
- .with_style(style.container.clone())
- })
- .on_click(MouseButton::Left, move |_, this, cx| {
- this.channel_store
- .update(cx, |channel_store, cx| {
- channel_store.set_channel_visibility(
- channel_id,
- ChannelVisibility::Public,
- cx,
- )
- })
- .detach_and_log_err(cx);
- })
- .with_cursor_style(CursorStyle::PointingHand),
- )
- .into_any();
- }
-
- Flex::row()
- .with_child(
- MouseEventHandler::new::<TogglePublic, _>(0, cx, move |state, _| {
- let style = theme.visibility_toggle.style_for(state);
- Label::new(format!("{}", "Public access: ON"), style.text.clone())
- .contained()
- .with_style(style.container.clone())
- })
- .on_click(MouseButton::Left, move |_, this, cx| {
- this.channel_store
- .update(cx, |channel_store, cx| {
- channel_store.set_channel_visibility(
- channel_id,
- ChannelVisibility::Members,
- cx,
- )
- })
- .detach_and_log_err(cx);
- })
- .with_cursor_style(CursorStyle::PointingHand),
- )
- .with_spacing(14.0)
- .with_child(
- MouseEventHandler::new::<TogglePublic, _>(1, cx, move |state, _| {
- let style = theme.channel_link.style_for(state);
- Label::new(format!("{}", "copy link"), style.text.clone())
- .contained()
- .with_style(style.container.clone())
- })
- .on_click(MouseButton::Left, move |_, this, cx| {
- if let Some(channel) =
- this.channel_store.read(cx).channel_for_id(channel_id)
- {
- let item = ClipboardItem::new(channel.link());
- cx.write_to_clipboard(item);
- }
- })
- .with_cursor_style(CursorStyle::PointingHand),
- )
- .into_any()
- }
-
- Flex::column()
- .with_child(
- Flex::column()
- .with_child(
- Label::new(format!("#{}", channel.name), theme.title.text.clone())
- .contained()
- .with_style(theme.title.container.clone()),
- )
- .with_child(render_visibility(channel.id, channel.visibility, theme, cx))
- .with_child(Flex::row().with_children([
- render_mode_button::<InviteMembers>(
- Mode::InviteMembers,
- "Invite members",
- mode,
- theme,
- cx,
- ),
- render_mode_button::<ManageMembers>(
- Mode::ManageMembers,
- "Manage members",
- mode,
- theme,
- cx,
- ),
- ]))
- .expanded()
- .contained()
- .with_style(theme.header),
- )
- .with_child(
- ChildView::new(&self.picker, cx)
- .contained()
- .with_style(theme.body),
- )
- .constrained()
- .with_max_height(theme.max_height)
- .with_max_width(theme.max_width)
- .contained()
- .with_style(theme.modal)
- .into_any()
- }
-
- fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
- self.has_focus = true;
- if cx.is_self_focused() {
- cx.focus(&self.picker)
- }
- }
+impl EventEmitter<DismissEvent> for ChannelModal {}
- fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
- self.has_focus = false;
+impl FocusableView for ChannelModal {
+ fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+ self.picker.focus_handle(cx)
}
}
-impl Modal for ChannelModal {
- fn has_focus(&self) -> bool {
- self.has_focus
+impl Render for ChannelModal {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ v_stack().min_w_96().child(self.picker.clone())
+ // let theme = &theme::current(cx).collab_panel.tabbed_modal;
+
+ // let mode = self.picker.read(cx).delegate().mode;
+ // let Some(channel) = self.channel_store.read(cx).channel_for_id(self.channel_id) else {
+ // return Empty::new().into_any();
+ // };
+
+ // enum InviteMembers {}
+ // enum ManageMembers {}
+
+ // fn render_mode_button<T: 'static>(
+ // mode: Mode,
+ // text: &'static str,
+ // current_mode: Mode,
+ // theme: &theme::TabbedModal,
+ // cx: &mut ViewContext<ChannelModal>,
+ // ) -> AnyElement<ChannelModal> {
+ // let active = mode == current_mode;
+ // MouseEventHandler::new::<T, _>(0, cx, move |state, _| {
+ // let contained_text = theme.tab_button.style_for(active, state);
+ // Label::new(text, contained_text.text.clone())
+ // .contained()
+ // .with_style(contained_text.container.clone())
+ // })
+ // .on_click(MouseButton::Left, move |_, this, cx| {
+ // if !active {
+ // this.set_mode(mode, cx);
+ // }
+ // })
+ // .with_cursor_style(CursorStyle::PointingHand)
+ // .into_any()
+ // }
+
+ // fn render_visibility(
+ // channel_id: ChannelId,
+ // visibility: ChannelVisibility,
+ // theme: &theme::TabbedModal,
+ // cx: &mut ViewContext<ChannelModal>,
+ // ) -> AnyElement<ChannelModal> {
+ // enum TogglePublic {}
+
+ // if visibility == ChannelVisibility::Members {
+ // return Flex::row()
+ // .with_child(
+ // MouseEventHandler::new::<TogglePublic, _>(0, cx, move |state, _| {
+ // let style = theme.visibility_toggle.style_for(state);
+ // Label::new(format!("{}", "Public access: OFF"), style.text.clone())
+ // .contained()
+ // .with_style(style.container.clone())
+ // })
+ // .on_click(MouseButton::Left, move |_, this, cx| {
+ // this.channel_store
+ // .update(cx, |channel_store, cx| {
+ // channel_store.set_channel_visibility(
+ // channel_id,
+ // ChannelVisibility::Public,
+ // cx,
+ // )
+ // })
+ // .detach_and_log_err(cx);
+ // })
+ // .with_cursor_style(CursorStyle::PointingHand),
+ // )
+ // .into_any();
+ // }
+
+ // Flex::row()
+ // .with_child(
+ // MouseEventHandler::new::<TogglePublic, _>(0, cx, move |state, _| {
+ // let style = theme.visibility_toggle.style_for(state);
+ // Label::new(format!("{}", "Public access: ON"), style.text.clone())
+ // .contained()
+ // .with_style(style.container.clone())
+ // })
+ // .on_click(MouseButton::Left, move |_, this, cx| {
+ // this.channel_store
+ // .update(cx, |channel_store, cx| {
+ // channel_store.set_channel_visibility(
+ // channel_id,
+ // ChannelVisibility::Members,
+ // cx,
+ // )
+ // })
+ // .detach_and_log_err(cx);
+ // })
+ // .with_cursor_style(CursorStyle::PointingHand),
+ // )
+ // .with_spacing(14.0)
+ // .with_child(
+ // MouseEventHandler::new::<TogglePublic, _>(1, cx, move |state, _| {
+ // let style = theme.channel_link.style_for(state);
+ // Label::new(format!("{}", "copy link"), style.text.clone())
+ // .contained()
+ // .with_style(style.container.clone())
+ // })
+ // .on_click(MouseButton::Left, move |_, this, cx| {
+ // if let Some(channel) =
+ // this.channel_store.read(cx).channel_for_id(channel_id)
+ // {
+ // let item = ClipboardItem::new(channel.link());
+ // cx.write_to_clipboard(item);
+ // }
+ // })
+ // .with_cursor_style(CursorStyle::PointingHand),
+ // )
+ // .into_any()
+ // }
+
+ // Flex::column()
+ // .with_child(
+ // Flex::column()
+ // .with_child(
+ // Label::new(format!("#{}", channel.name), theme.title.text.clone())
+ // .contained()
+ // .with_style(theme.title.container.clone()),
+ // )
+ // .with_child(render_visibility(channel.id, channel.visibility, theme, cx))
+ // .with_child(Flex::row().with_children([
+ // render_mode_button::<InviteMembers>(
+ // Mode::InviteMembers,
+ // "Invite members",
+ // mode,
+ // theme,
+ // cx,
+ // ),
+ // render_mode_button::<ManageMembers>(
+ // Mode::ManageMembers,
+ // "Manage members",
+ // mode,
+ // theme,
+ // cx,
+ // ),
+ // ]))
+ // .expanded()
+ // .contained()
+ // .with_style(theme.header),
+ // )
+ // .with_child(
+ // ChildView::new(&self.picker, cx)
+ // .contained()
+ // .with_style(theme.body),
+ // )
+ // .constrained()
+ // .with_max_height(theme.max_height)
+ // .with_max_width(theme.max_width)
+ // .contained()
+ // .with_style(theme.modal)
+ // .into_any()
}
- fn dismiss_on_event(event: &Self::Event) -> bool {
- match event {
- PickerEvent::Dismiss => true,
- }
- }
+ // fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
+ // self.has_focus = true;
+ // if cx.is_self_focused() {
+ // cx.focus(&self.picker)
+ // }
+ // }
+
+ // fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
+ // self.has_focus = false;
+ // }
}
#[derive(Copy, Clone, PartialEq)]
@@ -337,19 +321,22 @@ pub enum Mode {
}
pub struct ChannelModalDelegate {
+ channel_modal: WeakView<ChannelModal>,
matching_users: Vec<Arc<User>>,
matching_member_indices: Vec<usize>,
- user_store: ModelHandle<UserStore>,
- channel_store: ModelHandle<ChannelStore>,
+ user_store: Model<UserStore>,
+ channel_store: Model<ChannelStore>,
channel_id: ChannelId,
selected_index: usize,
mode: Mode,
match_candidates: Vec<StringMatchCandidate>,
members: Vec<ChannelMembership>,
- context_menu: ViewHandle<ContextMenu>,
+ // context_menu: ViewHandle<ContextMenu>,
}
impl PickerDelegate for ChannelModalDelegate {
+ type ListItem = Div;
+
fn placeholder_text(&self) -> Arc<str> {
"Search collaborator by username...".into()
}
@@ -382,19 +369,19 @@ impl PickerDelegate for ChannelModalDelegate {
}
}));
- let matches = cx.background().block(match_strings(
+ let matches = cx.background_executor().block(match_strings(
&self.match_candidates,
&query,
true,
usize::MAX,
&Default::default(),
- cx.background().clone(),
+ cx.background_executor().clone(),
));
cx.spawn(|picker, mut cx| async move {
picker
.update(&mut cx, |picker, cx| {
- let delegate = picker.delegate_mut();
+ let delegate = &mut picker.delegate;
delegate.matching_member_indices.clear();
delegate
.matching_member_indices
@@ -412,8 +399,7 @@ impl PickerDelegate for ChannelModalDelegate {
async {
let users = search_users.await?;
picker.update(&mut cx, |picker, cx| {
- let delegate = picker.delegate_mut();
- delegate.matching_users = users;
+ picker.delegate.matching_users = users;
cx.notify();
})?;
anyhow::Ok(())
@@ -445,138 +431,142 @@ impl PickerDelegate for ChannelModalDelegate {
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
- cx.emit(PickerEvent::Dismiss);
+ self.channel_modal
+ .update(cx, |_, cx| {
+ cx.emit(DismissEvent);
+ })
+ .ok();
}
fn render_match(
&self,
ix: usize,
- mouse_state: &mut MouseState,
selected: bool,
- cx: &gpui::AppContext,
- ) -> AnyElement<Picker<Self>> {
- let full_theme = &theme::current(cx);
- let theme = &full_theme.collab_panel.channel_modal;
- let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
- let (user, role) = self.user_at_index(ix).unwrap();
- let request_status = self.member_status(user.id, cx);
-
- let style = tabbed_modal
- .picker
- .item
- .in_state(selected)
- .style_for(mouse_state);
-
- let in_manage = matches!(self.mode, Mode::ManageMembers);
-
- let mut result = Flex::row()
- .with_children(user.avatar.clone().map(|avatar| {
- Image::from_data(avatar)
- .with_style(theme.contact_avatar)
- .aligned()
- .left()
- }))
- .with_child(
- Label::new(user.github_login.clone(), style.label.clone())
- .contained()
- .with_style(theme.contact_username)
- .aligned()
- .left(),
- )
- .with_children({
- (in_manage && request_status == Some(proto::channel_member::Kind::Invitee)).then(
- || {
- Label::new("Invited", theme.member_tag.text.clone())
- .contained()
- .with_style(theme.member_tag.container)
- .aligned()
- .left()
- },
- )
- })
- .with_children(if in_manage && role == Some(ChannelRole::Admin) {
- Some(
- Label::new("Admin", theme.member_tag.text.clone())
- .contained()
- .with_style(theme.member_tag.container)
- .aligned()
- .left(),
- )
- } else if in_manage && role == Some(ChannelRole::Guest) {
- Some(
- Label::new("Guest", theme.member_tag.text.clone())
- .contained()
- .with_style(theme.member_tag.container)
- .aligned()
- .left(),
- )
- } else {
- None
- })
- .with_children({
- let svg = match self.mode {
- Mode::ManageMembers => Some(
- Svg::new("icons/ellipsis.svg")
- .with_color(theme.member_icon.color)
- .constrained()
- .with_width(theme.member_icon.icon_width)
- .aligned()
- .constrained()
- .with_width(theme.member_icon.button_width)
- .with_height(theme.member_icon.button_width)
- .contained()
- .with_style(theme.member_icon.container),
- ),
- Mode::InviteMembers => match request_status {
- Some(proto::channel_member::Kind::Member) => Some(
- Svg::new("icons/check.svg")
- .with_color(theme.member_icon.color)
- .constrained()
- .with_width(theme.member_icon.icon_width)
- .aligned()
- .constrained()
- .with_width(theme.member_icon.button_width)
- .with_height(theme.member_icon.button_width)
- .contained()
- .with_style(theme.member_icon.container),
- ),
- Some(proto::channel_member::Kind::Invitee) => Some(
- Svg::new("icons/check.svg")
- .with_color(theme.invitee_icon.color)
- .constrained()
- .with_width(theme.invitee_icon.icon_width)
- .aligned()
- .constrained()
- .with_width(theme.invitee_icon.button_width)
- .with_height(theme.invitee_icon.button_width)
- .contained()
- .with_style(theme.invitee_icon.container),
- ),
- Some(proto::channel_member::Kind::AncestorMember) | None => None,
- },
- };
-
- svg.map(|svg| svg.aligned().flex_float().into_any())
- })
- .contained()
- .with_style(style.container)
- .constrained()
- .with_height(tabbed_modal.row_height)
- .into_any();
-
- if selected {
- result = Stack::new()
- .with_child(result)
- .with_child(
- ChildView::new(&self.context_menu, cx)
- .aligned()
- .top()
- .right(),
- )
- .into_any();
- }
-
- result
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> Option<Self::ListItem> {
+ None
+ // let full_theme = &theme::current(cx);
+ // let theme = &full_theme.collab_panel.channel_modal;
+ // let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
+ // let (user, role) = self.user_at_index(ix).unwrap();
+ // let request_status = self.member_status(user.id, cx);
+
+ // let style = tabbed_modal
+ // .picker
+ // .item
+ // .in_state(selected)
+ // .style_for(mouse_state);
+
+ // let in_manage = matches!(self.mode, Mode::ManageMembers);
+
+ // let mut result = Flex::row()
+ // .with_children(user.avatar.clone().map(|avatar| {
+ // Image::from_data(avatar)
+ // .with_style(theme.contact_avatar)
+ // .aligned()
+ // .left()
+ // }))
+ // .with_child(
+ // Label::new(user.github_login.clone(), style.label.clone())
+ // .contained()
+ // .with_style(theme.contact_username)
+ // .aligned()
+ // .left(),
+ // )
+ // .with_children({
+ // (in_manage && request_status == Some(proto::channel_member::Kind::Invitee)).then(
+ // || {
+ // Label::new("Invited", theme.member_tag.text.clone())
+ // .contained()
+ // .with_style(theme.member_tag.container)
+ // .aligned()
+ // .left()
+ // },
+ // )
+ // })
+ // .with_children(if in_manage && role == Some(ChannelRole::Admin) {
+ // Some(
+ // Label::new("Admin", theme.member_tag.text.clone())
+ // .contained()
+ // .with_style(theme.member_tag.container)
+ // .aligned()
+ // .left(),
+ // )
+ // } else if in_manage && role == Some(ChannelRole::Guest) {
+ // Some(
+ // Label::new("Guest", theme.member_tag.text.clone())
+ // .contained()
+ // .with_style(theme.member_tag.container)
+ // .aligned()
+ // .left(),
+ // )
+ // } else {
+ // None
+ // })
+ // .with_children({
+ // let svg = match self.mode {
+ // Mode::ManageMembers => Some(
+ // Svg::new("icons/ellipsis.svg")
+ // .with_color(theme.member_icon.color)
+ // .constrained()
+ // .with_width(theme.member_icon.icon_width)
+ // .aligned()
+ // .constrained()
+ // .with_width(theme.member_icon.button_width)
+ // .with_height(theme.member_icon.button_width)
+ // .contained()
+ // .with_style(theme.member_icon.container),
+ // ),
+ // Mode::InviteMembers => match request_status {
+ // Some(proto::channel_member::Kind::Member) => Some(
+ // Svg::new("icons/check.svg")
+ // .with_color(theme.member_icon.color)
+ // .constrained()
+ // .with_width(theme.member_icon.icon_width)
+ // .aligned()
+ // .constrained()
+ // .with_width(theme.member_icon.button_width)
+ // .with_height(theme.member_icon.button_width)
+ // .contained()
+ // .with_style(theme.member_icon.container),
+ // ),
+ // Some(proto::channel_member::Kind::Invitee) => Some(
+ // Svg::new("icons/check.svg")
+ // .with_color(theme.invitee_icon.color)
+ // .constrained()
+ // .with_width(theme.invitee_icon.icon_width)
+ // .aligned()
+ // .constrained()
+ // .with_width(theme.invitee_icon.button_width)
+ // .with_height(theme.invitee_icon.button_width)
+ // .contained()
+ // .with_style(theme.invitee_icon.container),
+ // ),
+ // Some(proto::channel_member::Kind::AncestorMember) | None => None,
+ // },
+ // };
+
+ // svg.map(|svg| svg.aligned().flex_float().into_any())
+ // })
+ // .contained()
+ // .with_style(style.container)
+ // .constrained()
+ // .with_height(tabbed_modal.row_height)
+ // .into_any();
+
+ // if selected {
+ // result = Stack::new()
+ // .with_child(result)
+ // .with_child(
+ // ChildView::new(&self.context_menu, cx)
+ // .aligned()
+ // .top()
+ // .right(),
+ // )
+ // .into_any();
+ // }
+
+ // result
}
}
@@ -623,7 +613,7 @@ impl ChannelModalDelegate {
cx.spawn(|picker, mut cx| async move {
update.await?;
picker.update(&mut cx, |picker, cx| {
- let this = picker.delegate_mut();
+ let this = &mut picker.delegate;
if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user.id) {
member.role = new_role;
}
@@ -644,7 +634,7 @@ impl ChannelModalDelegate {
cx.spawn(|picker, mut cx| async move {
update.await?;
picker.update(&mut cx, |picker, cx| {
- let this = picker.delegate_mut();
+ let this = &mut picker.delegate;
if let Some(ix) = this.members.iter_mut().position(|m| m.user.id == user_id) {
this.members.remove(ix);
this.matching_member_indices.retain_mut(|member_ix| {
@@ -683,7 +673,7 @@ impl ChannelModalDelegate {
kind: proto::channel_member::Kind::Invitee,
role: ChannelRole::Member,
};
- let members = &mut this.delegate_mut().members;
+ let members = &mut this.delegate.members;
match members.binary_search_by_key(&new_member.sort_key(), |k| k.sort_key()) {
Ok(ix) | Err(ix) => members.insert(ix, new_member),
}
@@ -695,23 +685,23 @@ impl ChannelModalDelegate {
}
fn show_context_menu(&mut self, role: ChannelRole, cx: &mut ViewContext<Picker<Self>>) {
- self.context_menu.update(cx, |context_menu, cx| {
- context_menu.show(
- Default::default(),
- AnchorCorner::TopRight,
- vec![
- ContextMenuItem::action("Remove", RemoveMember),
- ContextMenuItem::action(
- if role == ChannelRole::Admin {
- "Make non-admin"
- } else {
- "Make admin"
- },
- ToggleMemberAdmin,
- ),
- ],
- cx,
- )
- })
+ // self.context_menu.update(cx, |context_menu, cx| {
+ // context_menu.show(
+ // Default::default(),
+ // AnchorCorner::TopRight,
+ // vec![
+ // ContextMenuItem::action("Remove", RemoveMember),
+ // ContextMenuItem::action(
+ // if role == ChannelRole::Admin {
+ // "Make non-admin"
+ // } else {
+ // "Make admin"
+ // },
+ // ToggleMemberAdmin,
+ // ),
+ // ],
+ // cx,
+ // )
+ // })
}
}
@@ -35,16 +35,19 @@ use gpui::{
ParentElement, Render, RenderOnce, Stateful, StatefulInteractiveElement, Styled, Subscription,
ViewContext, VisualContext, WeakView, WindowBounds,
};
-use project::Project;
+use project::{Project, RepositoryEntry};
use theme::ActiveTheme;
-use ui::{h_stack, prelude::*, Avatar, Button, ButtonStyle, IconButton, KeyBinding, Tooltip};
+use ui::{
+ h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
+ IconButton, IconElement, KeyBinding, Tooltip,
+};
use util::ResultExt;
use workspace::{notifications::NotifyResultExt, Workspace};
use crate::face_pile::FacePile;
-// const MAX_PROJECT_NAME_LENGTH: usize = 40;
-// const MAX_BRANCH_NAME_LENGTH: usize = 40;
+const MAX_PROJECT_NAME_LENGTH: usize = 40;
+const MAX_BRANCH_NAME_LENGTH: usize = 40;
// actions!(
// collab,
@@ -100,17 +103,18 @@ impl Render for CollabTitlebarItem {
.update(cx, |this, cx| this.call_state().remote_participants(cx))
.log_err()
.flatten();
- let mic_icon = if self
+ let is_muted = self
.workspace
.update(cx, |this, cx| this.call_state().is_muted(cx))
.log_err()
.flatten()
- .unwrap_or_default()
- {
- ui::Icon::MicMute
- } else {
- ui::Icon::Mic
- };
+ .unwrap_or_default();
+ let is_deafened = self
+ .workspace
+ .update(cx, |this, cx| this.call_state().is_deafened(cx))
+ .log_err()
+ .flatten()
+ .unwrap_or_default();
let speakers_icon = if self
.workspace
.update(cx, |this, cx| this.call_state().is_deafened(cx))
@@ -146,56 +150,11 @@ impl Render for CollabTitlebarItem {
.child(
h_stack()
.gap_1()
- // TODO - Add player menu
- .child(
- div()
- .border()
- .border_color(gpui::red())
- .id("project_owner_indicator")
- .child(
- Button::new("player", "player")
- .style(ButtonStyle::Subtle)
- .color(Some(Color::Player(0))),
- )
- .tooltip(move |cx| Tooltip::text("Toggle following", cx)),
- )
- // TODO - Add project menu
- .child(
- div()
- .border()
- .border_color(gpui::red())
- .id("titlebar_project_menu_button")
- .child(
- Button::new("project_name", "project_name")
- .style(ButtonStyle::Subtle),
- )
- .tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
- )
- // TODO - Add git menu
- .child(
- div()
- .border()
- .border_color(gpui::red())
- .id("titlebar_git_menu_button")
- .child(
- Button::new("branch_name", "branch_name")
- .style(ButtonStyle::Subtle)
- .color(Some(Color::Muted)),
- )
- .tooltip(move |cx| {
- cx.build_view(|_| {
- Tooltip::new("Recent Branches")
- .key_binding(KeyBinding::new(gpui::KeyBinding::new(
- "cmd-b",
- // todo!() Replace with real action.
- gpui::NoAction,
- None,
- )))
- .meta("Only local branches shown")
- })
- .into()
- }),
- ),
+ .when(is_in_room, |this| {
+ this.children(self.render_project_owner(cx))
+ })
+ .child(self.render_project_name(cx))
+ .children(self.render_project_branch(cx)),
)
.when_some(
users.zip(current_user.clone()),
@@ -236,62 +195,126 @@ impl Render for CollabTitlebarItem {
.when(is_in_room, |this| {
this.child(
h_stack()
+ .gap_1()
.child(
h_stack()
- .child(Button::new(
- "toggle_sharing",
- if is_shared { "Unshare" } else { "Share" },
- ))
- .child(IconButton::new("leave-call", ui::Icon::Exit).on_click({
- let workspace = workspace.clone();
- move |_, cx| {
- workspace
- .update(cx, |this, cx| {
- this.call_state().hang_up(cx).detach();
- })
- .log_err();
- }
- })),
+ .gap_1()
+ .child(
+ Button::new(
+ "toggle_sharing",
+ if is_shared { "Unshare" } else { "Share" },
+ )
+ .style(ButtonStyle::Subtle),
+ )
+ .child(
+ IconButton::new("leave-call", ui::Icon::Exit)
+ .style(ButtonStyle::Subtle)
+ .on_click({
+ let workspace = workspace.clone();
+ move |_, cx| {
+ workspace
+ .update(cx, |this, cx| {
+ this.call_state().hang_up(cx).detach();
+ })
+ .log_err();
+ }
+ }),
+ ),
)
.child(
h_stack()
- .child(IconButton::new("mute-microphone", mic_icon).on_click({
- let workspace = workspace.clone();
- move |_, cx| {
- workspace
- .update(cx, |this, cx| {
- this.call_state().toggle_mute(cx);
- })
- .log_err();
- }
- }))
- .child(IconButton::new("mute-sound", speakers_icon).on_click({
- let workspace = workspace.clone();
- move |_, cx| {
- workspace
- .update(cx, |this, cx| {
- this.call_state().toggle_deafen(cx);
- })
- .log_err();
- }
- }))
- .child(IconButton::new("screen-share", ui::Icon::Screen).on_click(
- move |_, cx| {
- workspace
- .update(cx, |this, cx| {
- this.call_state().toggle_screen_share(cx);
- })
- .log_err();
- },
- ))
+ .gap_1()
+ .child(
+ IconButton::new(
+ "mute-microphone",
+ if is_muted {
+ ui::Icon::MicMute
+ } else {
+ ui::Icon::Mic
+ },
+ )
+ .style(ButtonStyle::Subtle)
+ .selected(is_muted)
+ .on_click({
+ let workspace = workspace.clone();
+ move |_, cx| {
+ workspace
+ .update(cx, |this, cx| {
+ this.call_state().toggle_mute(cx);
+ })
+ .log_err();
+ }
+ }),
+ )
+ .child(
+ IconButton::new("mute-sound", speakers_icon)
+ .style(ButtonStyle::Subtle)
+ .selected(is_deafened.clone())
+ .tooltip(move |cx| {
+ Tooltip::with_meta(
+ "Deafen Audio",
+ None,
+ "Mic will be muted",
+ cx,
+ )
+ })
+ .on_click({
+ let workspace = workspace.clone();
+ move |_, cx| {
+ workspace
+ .update(cx, |this, cx| {
+ this.call_state().toggle_deafen(cx);
+ })
+ .log_err();
+ }
+ }),
+ )
+ .child(
+ IconButton::new("screen-share", ui::Icon::Screen)
+ .style(ButtonStyle::Subtle)
+ .on_click(move |_, cx| {
+ workspace
+ .update(cx, |this, cx| {
+ this.call_state().toggle_screen_share(cx);
+ })
+ .log_err();
+ }),
+ )
.pl_2(),
),
)
})
- .map(|this| {
+ .child(h_stack().px_1p5().map(|this| {
if let Some(user) = current_user {
this.when_some(user.avatar.clone(), |this, avatar| {
- this.child(ui::Avatar::data(avatar))
+ // TODO: Finish implementing user menu popover
+ //
+ this.child(
+ popover_menu("user-menu")
+ .menu(|cx| ContextMenu::build(cx, |menu, _| menu.header("ADADA")))
+ .trigger(
+ ButtonLike::new("user-menu")
+ .child(
+ h_stack().gap_0p5().child(Avatar::data(avatar)).child(
+ IconElement::new(Icon::ChevronDown)
+ .color(Color::Muted),
+ ),
+ )
+ .style(ButtonStyle::Subtle)
+ .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+ )
+ .anchor(gpui::AnchorCorner::TopRight),
+ )
+ // this.child(
+ // ButtonLike::new("user-menu")
+ // .child(
+ // h_stack().gap_0p5().child(Avatar::data(avatar)).child(
+ // IconElement::new(Icon::ChevronDown).color(Color::Muted),
+ // ),
+ // )
+ // .style(ButtonStyle::Subtle)
+ // .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+ // )
})
} else {
this.child(Button::new("sign_in", "Sign in").on_click(move |_, cx| {
@@ -305,7 +328,7 @@ impl Render for CollabTitlebarItem {
.detach();
}))
}
- })
+ }))
}
}
@@ -424,6 +447,110 @@ impl CollabTitlebarItem {
}
}
+ // resolve if you are in a room -> render_project_owner
+ // render_project_owner -> resolve if you are in a room -> Option<foo>
+
+ pub fn render_project_owner(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
+ // TODO: We can't finish implementing this until project sharing works
+ // - [ ] Show the project owner when the project is remote (maybe done)
+ // - [x] Show the project owner when the project is local
+ // - [ ] Show the project owner with a lock icon when the project is local and unshared
+
+ let remote_id = self.project.read(cx).remote_id();
+ let is_local = remote_id.is_none();
+ let is_shared = self.project.read(cx).is_shared();
+ let (user_name, participant_index) = {
+ if let Some(host) = self.project.read(cx).host() {
+ debug_assert!(!is_local);
+ let (Some(host_user), Some(participant_index)) = (
+ self.user_store.read(cx).get_cached_user(host.user_id),
+ self.user_store
+ .read(cx)
+ .participant_indices()
+ .get(&host.user_id),
+ ) else {
+ return None;
+ };
+ (host_user.github_login.clone(), participant_index.0)
+ } else {
+ debug_assert!(is_local);
+ let name = self
+ .user_store
+ .read(cx)
+ .current_user()
+ .map(|user| user.github_login.clone())?;
+ (name, 0)
+ }
+ };
+ Some(
+ div().border().border_color(gpui::red()).child(
+ Button::new(
+ "project_owner_trigger",
+ format!("{user_name} ({})", !is_shared),
+ )
+ .color(Color::Player(participant_index))
+ .style(ButtonStyle::Subtle)
+ .tooltip(move |cx| Tooltip::text("Toggle following", cx)),
+ ),
+ )
+ }
+
+ pub fn render_project_name(&self, cx: &mut ViewContext<Self>) -> impl Element {
+ let name = {
+ let mut names = self.project.read(cx).visible_worktrees(cx).map(|worktree| {
+ let worktree = worktree.read(cx);
+ worktree.root_name()
+ });
+
+ names.next().unwrap_or("")
+ };
+
+ let name = util::truncate_and_trailoff(name, MAX_PROJECT_NAME_LENGTH);
+
+ div().border().border_color(gpui::red()).child(
+ Button::new("project_name_trigger", name)
+ .style(ButtonStyle::Subtle)
+ .tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
+ )
+ }
+
+ pub fn render_project_branch(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
+ let entry = {
+ let mut names_and_branches =
+ self.project.read(cx).visible_worktrees(cx).map(|worktree| {
+ let worktree = worktree.read(cx);
+ worktree.root_git_entry()
+ });
+
+ names_and_branches.next().flatten()
+ };
+
+ let branch_name = entry
+ .as_ref()
+ .and_then(RepositoryEntry::branch)
+ .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH))?;
+
+ Some(
+ div().border().border_color(gpui::red()).child(
+ Button::new("project_branch_trigger", branch_name)
+ .style(ButtonStyle::Subtle)
+ .tooltip(move |cx| {
+ cx.build_view(|_| {
+ Tooltip::new("Recent Branches")
+ .key_binding(KeyBinding::new(gpui::KeyBinding::new(
+ "cmd-b",
+ // todo!() Replace with real action.
+ gpui::NoAction,
+ None,
+ )))
+ .meta("Local branches only")
+ })
+ .into()
+ }),
+ ),
+ )
+ }
+
// fn collect_title_root_names(
// &self,
// theme: Arc<Theme>,
@@ -0,0 +1,27 @@
+[package]
+name = "copilot_button2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/copilot_button.rs"
+doctest = false
+
+[dependencies]
+copilot = { package = "copilot2", path = "../copilot2" }
+editor = { package = "editor2", path = "../editor2" }
+fs = { package = "fs2", path = "../fs2" }
+zed-actions = { package="zed_actions2", path = "../zed_actions2"}
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+settings = { package = "settings2", path = "../settings2" }
+theme = { package = "theme2", path = "../theme2" }
+util = { path = "../util" }
+workspace = { package = "workspace2", path = "../workspace2" }
+anyhow.workspace = true
+smol.workspace = true
+futures.workspace = true
+
+[dev-dependencies]
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
@@ -0,0 +1,371 @@
+#![allow(unused)]
+use anyhow::Result;
+use copilot::{Copilot, SignOut, Status};
+use editor::{scroll::autoscroll::Autoscroll, Editor};
+use fs::Fs;
+use gpui::{
+ div, Action, AnchorCorner, AppContext, AsyncAppContext, AsyncWindowContext, Div, Entity,
+ ParentElement, Render, Subscription, View, ViewContext, WeakView, WindowContext,
+};
+use language::{
+ language_settings::{self, all_language_settings, AllLanguageSettings},
+ File, Language,
+};
+use settings::{update_settings_file, Settings, SettingsStore};
+use std::{path::Path, sync::Arc};
+use util::{paths, ResultExt};
+use workspace::{
+ create_and_open_local_file,
+ item::ItemHandle,
+ ui::{
+ popover_menu, ButtonCommon, Clickable, ContextMenu, Icon, IconButton, PopoverMenu, Tooltip,
+ },
+ StatusItemView, Toast, Workspace,
+};
+use zed_actions::OpenBrowser;
+
+const COPILOT_SETTINGS_URL: &str = "https://github.com/settings/copilot";
+const COPILOT_STARTING_TOAST_ID: usize = 1337;
+const COPILOT_ERROR_TOAST_ID: usize = 1338;
+
+pub struct CopilotButton {
+ editor_subscription: Option<(Subscription, usize)>,
+ editor_enabled: Option<bool>,
+ language: Option<Arc<Language>>,
+ file: Option<Arc<dyn File>>,
+ fs: Arc<dyn Fs>,
+}
+
+impl Render for CopilotButton {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let all_language_settings = all_language_settings(None, cx);
+ if !all_language_settings.copilot.feature_enabled {
+ return div();
+ }
+
+ let Some(copilot) = Copilot::global(cx) else {
+ return div();
+ };
+ let status = copilot.read(cx).status();
+
+ let enabled = self
+ .editor_enabled
+ .unwrap_or_else(|| all_language_settings.copilot_enabled(None, None));
+
+ let icon = match status {
+ Status::Error(_) => Icon::CopilotError,
+ Status::Authorized => {
+ if enabled {
+ Icon::Copilot
+ } else {
+ Icon::CopilotDisabled
+ }
+ }
+ _ => Icon::CopilotInit,
+ };
+
+ if let Status::Error(e) = status {
+ return div().child(
+ IconButton::new("copilot-error", icon)
+ .on_click(cx.listener(move |this, _, cx| {
+ if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
+ workspace.update(cx, |workspace, cx| {
+ workspace.show_toast(
+ Toast::new(
+ COPILOT_ERROR_TOAST_ID,
+ format!("Copilot can't be started: {}", e),
+ )
+ .on_click(
+ "Reinstall Copilot",
+ |cx| {
+ if let Some(copilot) = Copilot::global(cx) {
+ copilot
+ .update(cx, |copilot, cx| copilot.reinstall(cx))
+ .detach();
+ }
+ },
+ ),
+ cx,
+ );
+ });
+ }
+ }))
+ .tooltip(|cx| Tooltip::text("GitHub Copilot", cx)),
+ );
+ }
+ let this = cx.view().clone();
+
+ div().child(
+ popover_menu("copilot")
+ .menu(move |cx| match status {
+ Status::Authorized => this.update(cx, |this, cx| this.build_copilot_menu(cx)),
+ _ => this.update(cx, |this, cx| this.build_copilot_start_menu(cx)),
+ })
+ .anchor(AnchorCorner::BottomRight)
+ .trigger(
+ IconButton::new("copilot-icon", icon)
+ .tooltip(|cx| Tooltip::text("GitHub Copilot", cx)),
+ ),
+ )
+ }
+}
+
+impl CopilotButton {
+ pub fn new(fs: Arc<dyn Fs>, cx: &mut ViewContext<Self>) -> Self {
+ Copilot::global(cx).map(|copilot| cx.observe(&copilot, |_, _, cx| cx.notify()).detach());
+
+ cx.observe_global::<SettingsStore>(move |_, cx| cx.notify())
+ .detach();
+
+ Self {
+ editor_subscription: None,
+ editor_enabled: None,
+ language: None,
+ file: None,
+ fs,
+ }
+ }
+
+ pub fn build_copilot_start_menu(&mut self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
+ let fs = self.fs.clone();
+ ContextMenu::build(cx, |menu, cx| {
+ menu.entry("Sign In", initiate_sign_in)
+ .entry("Disable Copilot", move |cx| hide_copilot(fs.clone(), cx))
+ })
+ }
+
+ pub fn build_copilot_menu(&mut self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
+ let fs = self.fs.clone();
+
+ return ContextMenu::build(cx, move |mut menu, cx| {
+ if let Some(language) = self.language.clone() {
+ let fs = fs.clone();
+ let language_enabled =
+ language_settings::language_settings(Some(&language), None, cx)
+ .show_copilot_suggestions;
+
+ menu = menu.entry(
+ format!(
+ "{} Suggestions for {}",
+ if language_enabled { "Hide" } else { "Show" },
+ language.name()
+ ),
+ move |cx| toggle_copilot_for_language(language.clone(), fs.clone(), cx),
+ );
+ }
+
+ let settings = AllLanguageSettings::get_global(cx);
+
+ if let Some(file) = &self.file {
+ let path = file.path().clone();
+ let path_enabled = settings.copilot_enabled_for_path(&path);
+
+ menu = menu.entry(
+ format!(
+ "{} Suggestions for This Path",
+ if path_enabled { "Hide" } else { "Show" }
+ ),
+ move |cx| {
+ if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
+ if let Ok(workspace) = workspace.root_view(cx) {
+ let workspace = workspace.downgrade();
+ cx.spawn(|cx| {
+ configure_disabled_globs(
+ workspace,
+ path_enabled.then_some(path.clone()),
+ cx,
+ )
+ })
+ .detach_and_log_err(cx);
+ }
+ }
+ },
+ );
+ }
+
+ let globally_enabled = settings.copilot_enabled(None, None);
+ menu.entry(
+ if globally_enabled {
+ "Hide Suggestions for All Files"
+ } else {
+ "Show Suggestions for All Files"
+ },
+ move |cx| toggle_copilot_globally(fs.clone(), cx),
+ )
+ .separator()
+ .link(
+ "Copilot Settings",
+ OpenBrowser {
+ url: COPILOT_SETTINGS_URL.to_string(),
+ }
+ .boxed_clone(),
+ cx,
+ )
+ .action("Sign Out", SignOut.boxed_clone(), cx)
+ });
+ }
+
+ pub fn update_enabled(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+ let editor = editor.read(cx);
+ let snapshot = editor.buffer().read(cx).snapshot(cx);
+ let suggestion_anchor = editor.selections.newest_anchor().start;
+ let language = snapshot.language_at(suggestion_anchor);
+ let file = snapshot.file_at(suggestion_anchor).cloned();
+
+ self.editor_enabled = Some(
+ all_language_settings(self.file.as_ref(), cx)
+ .copilot_enabled(language, file.as_ref().map(|file| file.path().as_ref())),
+ );
+ self.language = language.cloned();
+ self.file = file;
+
+ cx.notify()
+ }
+}
+
+impl StatusItemView for CopilotButton {
+ fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
+ if let Some(editor) = item.map(|item| item.act_as::<Editor>(cx)).flatten() {
+ self.editor_subscription = Some((
+ cx.observe(&editor, Self::update_enabled),
+ editor.entity_id().as_u64() as usize,
+ ));
+ self.update_enabled(editor, cx);
+ } else {
+ self.language = None;
+ self.editor_subscription = None;
+ self.editor_enabled = None;
+ }
+ cx.notify();
+ }
+}
+
+async fn configure_disabled_globs(
+ workspace: WeakView<Workspace>,
+ path_to_disable: Option<Arc<Path>>,
+ mut cx: AsyncWindowContext,
+) -> Result<()> {
+ let settings_editor = workspace
+ .update(&mut cx, |_, cx| {
+ create_and_open_local_file(&paths::SETTINGS, cx, || {
+ settings::initial_user_settings_content().as_ref().into()
+ })
+ })?
+ .await?
+ .downcast::<Editor>()
+ .unwrap();
+
+ settings_editor.downgrade().update(&mut cx, |item, cx| {
+ let text = item.buffer().read(cx).snapshot(cx).text();
+
+ let settings = cx.global::<SettingsStore>();
+ let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
+ let copilot = file.copilot.get_or_insert_with(Default::default);
+ let globs = copilot.disabled_globs.get_or_insert_with(|| {
+ settings
+ .get::<AllLanguageSettings>(None)
+ .copilot
+ .disabled_globs
+ .iter()
+ .map(|glob| glob.glob().to_string())
+ .collect()
+ });
+
+ if let Some(path_to_disable) = &path_to_disable {
+ globs.push(path_to_disable.to_string_lossy().into_owned());
+ } else {
+ globs.clear();
+ }
+ });
+
+ if !edits.is_empty() {
+ item.change_selections(Some(Autoscroll::newest()), cx, |selections| {
+ selections.select_ranges(edits.iter().map(|e| e.0.clone()));
+ });
+
+ // When *enabling* a path, don't actually perform an edit, just select the range.
+ if path_to_disable.is_some() {
+ item.edit(edits.iter().cloned(), cx);
+ }
+ }
+ })?;
+
+ anyhow::Ok(())
+}
+
+fn toggle_copilot_globally(fs: Arc<dyn Fs>, cx: &mut AppContext) {
+ let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None);
+ update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
+ file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
+ });
+}
+
+fn toggle_copilot_for_language(language: Arc<Language>, fs: Arc<dyn Fs>, cx: &mut AppContext) {
+ let show_copilot_suggestions =
+ all_language_settings(None, cx).copilot_enabled(Some(&language), None);
+ update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
+ file.languages
+ .entry(language.name())
+ .or_default()
+ .show_copilot_suggestions = Some(!show_copilot_suggestions);
+ });
+}
+
+fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut AppContext) {
+ update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
+ file.features.get_or_insert(Default::default()).copilot = Some(false);
+ });
+}
+
+fn initiate_sign_in(cx: &mut WindowContext) {
+ let Some(copilot) = Copilot::global(cx) else {
+ return;
+ };
+ let status = copilot.read(cx).status();
+
+ match status {
+ Status::Starting { task } => {
+ let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
+ return;
+ };
+
+ let Ok(workspace) = workspace.update(cx, |workspace, cx| {
+ workspace.show_toast(
+ Toast::new(COPILOT_STARTING_TOAST_ID, "Copilot is starting..."),
+ cx,
+ );
+ workspace.weak_handle()
+ }) else {
+ return;
+ };
+
+ cx.spawn(|mut cx| async move {
+ task.await;
+ if let Some(copilot) = cx.update(|_, cx| Copilot::global(cx)).ok().flatten() {
+ workspace
+ .update(&mut cx, |workspace, cx| match copilot.read(cx).status() {
+ Status::Authorized => workspace.show_toast(
+ Toast::new(COPILOT_STARTING_TOAST_ID, "Copilot has started!"),
+ cx,
+ ),
+ _ => {
+ workspace.dismiss_toast(COPILOT_STARTING_TOAST_ID, cx);
+ copilot
+ .update(cx, |copilot, cx| copilot.sign_in(cx))
+ .detach_and_log_err(cx);
+ }
+ })
+ .log_err();
+ }
+ })
+ .detach();
+ }
+ _ => {
+ copilot
+ .update(cx, |copilot, cx| copilot.sign_in(cx))
+ .detach_and_log_err(cx);
+ }
+ }
+}
@@ -774,24 +774,39 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
Arc::new(move |_| {
h_stack()
.id("diagnostic header")
- .gap_3()
- .bg(gpui::red())
- .map(|stack| {
- let icon = if diagnostic.severity == DiagnosticSeverity::ERROR {
- IconElement::new(Icon::XCircle).color(Color::Error)
- } else {
- IconElement::new(Icon::ExclamationTriangle).color(Color::Warning)
- };
-
- stack.child(div().pl_8().child(icon))
- })
- .when_some(diagnostic.source.as_ref(), |stack, source| {
- stack.child(Label::new(format!("{source}:")).color(Color::Accent))
- })
- .child(HighlightedLabel::new(message.clone(), highlights.clone()))
- .when_some(diagnostic.code.as_ref(), |stack, code| {
- stack.child(Label::new(code.clone()))
- })
+ .py_2()
+ .pl_10()
+ .pr_5()
+ .w_full()
+ .justify_between()
+ .gap_2()
+ .child(
+ h_stack()
+ .gap_3()
+ .map(|stack| {
+ let icon = if diagnostic.severity == DiagnosticSeverity::ERROR {
+ IconElement::new(Icon::XCircle).color(Color::Error)
+ } else {
+ IconElement::new(Icon::ExclamationTriangle).color(Color::Warning)
+ };
+ stack.child(icon)
+ })
+ .child(
+ h_stack()
+ .gap_1()
+ .child(HighlightedLabel::new(message.clone(), highlights.clone()))
+ .when_some(diagnostic.code.as_ref(), |stack, code| {
+ stack.child(Label::new(format!("({code})")).color(Color::Muted))
+ }),
+ ),
+ )
+ .child(
+ h_stack()
+ .gap_1()
+ .when_some(diagnostic.source.as_ref(), |stack, source| {
+ stack.child(Label::new(format!("{source}")).color(Color::Muted))
+ }),
+ )
.into_any_element()
})
}
@@ -802,11 +817,22 @@ pub(crate) fn render_summary(summary: &DiagnosticSummary) -> AnyElement {
label.into_any_element()
} else {
h_stack()
- .bg(gpui::red())
- .child(IconElement::new(Icon::XCircle))
- .child(Label::new(summary.error_count.to_string()))
- .child(IconElement::new(Icon::ExclamationTriangle))
- .child(Label::new(summary.warning_count.to_string()))
+ .gap_1()
+ .when(summary.error_count > 0, |then| {
+ then.child(
+ h_stack()
+ .gap_1()
+ .child(IconElement::new(Icon::XCircle).color(Color::Error))
+ .child(Label::new(summary.error_count.to_string())),
+ )
+ })
+ .when(summary.warning_count > 0, |then| {
+ then.child(
+ h_stack()
+ .child(IconElement::new(Icon::ExclamationTriangle).color(Color::Warning))
+ .child(Label::new(summary.warning_count.to_string())),
+ )
+ })
.into_any_element()
}
}
@@ -100,8 +100,10 @@ use text::{OffsetUtf16, Rope};
use theme::{
ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
};
-use ui::prelude::*;
-use ui::{h_stack, v_stack, HighlightedLabel, IconButton, Popover, Tooltip};
+use ui::{
+ h_stack, v_stack, ButtonSize, ButtonStyle, HighlightedLabel, Icon, IconButton, Popover, Tooltip,
+};
+use ui::{prelude::*, IconSize};
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::{
item::{ItemEvent, ItemHandle},
@@ -154,7 +156,6 @@ pub fn render_parsed_markdown(
}
}),
);
- let runs = text_runs_for_highlights(&parsed.text, &editor_style.text, highlights);
let mut links = Vec::new();
let mut link_ranges = Vec::new();
@@ -167,7 +168,7 @@ pub fn render_parsed_markdown(
InteractiveText::new(
element_id,
- StyledText::new(parsed.text.clone()).with_runs(runs),
+ StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
)
.on_click(link_ranges, move |clicked_range_ix, cx| {
match &links[clicked_range_ix] {
@@ -1199,11 +1200,7 @@ impl CompletionsMenu {
),
);
let completion_label = StyledText::new(completion.label.text.clone())
- .with_runs(text_runs_for_highlights(
- &completion.label.text,
- &style.text,
- highlights,
- ));
+ .with_highlights(&style.text, highlights);
let documentation_label =
if let Some(Documentation::SingleLine(text)) = documentation {
Some(SharedString::from(text.clone()))
@@ -1925,14 +1922,14 @@ impl Editor {
// self.buffer.read(cx).read(cx).file_at(point).cloned()
// }
- // pub fn active_excerpt(
- // &self,
- // cx: &AppContext,
- // ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
- // self.buffer
- // .read(cx)
- // .excerpt_containing(self.selections.newest_anchor().head(), cx)
- // }
+ pub fn active_excerpt(
+ &self,
+ cx: &AppContext,
+ ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
+ self.buffer
+ .read(cx)
+ .excerpt_containing(self.selections.newest_anchor().head(), cx)
+ }
// pub fn style(&self, cx: &AppContext) -> EditorStyle {
// build_style(
@@ -9699,20 +9696,42 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
let message = diagnostic.message;
Arc::new(move |cx: &mut BlockContext| {
let message = message.clone();
+ let copy_id: SharedString = format!("copy-{}", cx.block_id.clone()).to_string().into();
+ let write_to_clipboard = cx.write_to_clipboard(ClipboardItem::new(message.clone()));
+
+ // TODO: Nate: We should tint the background of the block with the severity color
+ // We need to extend the theme before we can do this
v_stack()
.id(cx.block_id)
+ .relative()
.size_full()
.bg(gpui::red())
.children(highlighted_lines.iter().map(|(line, highlights)| {
- div()
+ let group_id = cx.block_id.to_string();
+
+ h_stack()
+ .group(group_id.clone())
+ .gap_2()
+ .absolute()
+ .left(cx.anchor_x)
+ .px_1p5()
.child(HighlightedLabel::new(line.clone(), highlights.clone()))
- .ml(cx.anchor_x)
- }))
- .cursor_pointer()
- .on_click(cx.listener(move |_, _, cx| {
- cx.write_to_clipboard(ClipboardItem::new(message.clone()));
+ .child(
+ div()
+ .border()
+ .border_color(gpui::red())
+ .invisible()
+ .group_hover(group_id, |style| style.visible())
+ .child(
+ IconButton::new(copy_id.clone(), Icon::Copy)
+ .icon_color(Color::Muted)
+ .size(ButtonSize::Compact)
+ .style(ButtonStyle::Transparent)
+ .on_click(cx.listener(move |_, _, cx| write_to_clipboard))
+ .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
+ ),
+ )
}))
- .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx))
.into_any_element()
})
}
@@ -9760,31 +9779,6 @@ pub fn diagnostic_style(
}
}
-pub fn text_runs_for_highlights(
- text: &str,
- default_style: &TextStyle,
- highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
-) -> Vec<TextRun> {
- let mut runs = Vec::new();
- let mut ix = 0;
- for (range, highlight) in highlights {
- if ix < range.start {
- runs.push(default_style.clone().to_run(range.start - ix));
- }
- runs.push(
- default_style
- .clone()
- .highlight(highlight)
- .to_run(range.len()),
- );
- ix = range.end;
- }
- if ix < text.len() {
- runs.push(default_style.to_run(text.len() - ix));
- }
- runs
-}
-
pub fn styled_runs_for_code_label<'a>(
label: &'a CodeLabel,
syntax_theme: &'a theme::SyntaxTheme,
@@ -51,8 +51,10 @@ use std::{
};
use sum_tree::Bias;
use theme::{ActiveTheme, PlayerColor};
-use ui::prelude::*;
-use ui::{h_stack, IconButton, Tooltip};
+use ui::{
+ h_stack, ButtonLike, ButtonStyle, Disclosure, IconButton, IconElement, IconSize, Label, Tooltip,
+};
+use ui::{prelude::*, Icon};
use util::ResultExt;
use workspace::item::Item;
@@ -2223,7 +2225,8 @@ impl EditorElement {
.as_ref()
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
.unwrap_or_default();
- let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
+
+ let jump_handler = project::File::from_dyn(buffer.file()).map(|file| {
let jump_path = ProjectPath {
worktree_id: file.worktree_id(cx),
path: file.path.clone(),
@@ -2234,11 +2237,11 @@ impl EditorElement {
.map_or(range.context.start, |primary| primary.start);
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
- IconButton::new(block_id, ui::Icon::ArrowUpRight)
- .on_click(cx.listener_for(&self.editor, move |editor, e, cx| {
- editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
- }))
- .tooltip(|cx| Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx))
+ let jump_handler = cx.listener_for(&self.editor, move |editor, e, cx| {
+ editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
+ });
+
+ jump_handler
});
let element = if *starts_new_buffer {
@@ -2253,25 +2256,108 @@ impl EditorElement {
.map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
}
- h_stack()
- .id("path header block")
- .size_full()
- .bg(gpui::red())
- .child(
- filename
- .map(SharedString::from)
- .unwrap_or_else(|| "untitled".into()),
- )
- .children(parent_path)
- .children(jump_icon) // .p_x(gutter_padding)
+ let is_open = true;
+
+ div().id("path header container").size_full().p_1p5().child(
+ h_stack()
+ .id("path header block")
+ .py_1p5()
+ .pl_3()
+ .pr_2()
+ .rounded_lg()
+ .shadow_md()
+ .border()
+ .border_color(cx.theme().colors().border)
+ .bg(cx.theme().colors().editor_subheader_background)
+ .justify_between()
+ .cursor_pointer()
+ .hover(|style| style.bg(cx.theme().colors().element_hover))
+ .on_click(cx.listener(|_editor, _event, _cx| {
+ // TODO: Implement collapsing path headers
+ todo!("Clicking path header")
+ }))
+ .child(
+ h_stack()
+ .gap_3()
+ // TODO: Add open/close state and toggle action
+ .child(
+ div().border().border_color(gpui::red()).child(
+ ButtonLike::new("path-header-disclosure-control")
+ .style(ButtonStyle::Subtle)
+ .child(IconElement::new(match is_open {
+ true => Icon::ChevronDown,
+ false => Icon::ChevronRight,
+ })),
+ ),
+ )
+ .child(
+ h_stack()
+ .gap_2()
+ .child(Label::new(
+ filename
+ .map(SharedString::from)
+ .unwrap_or_else(|| "untitled".into()),
+ ))
+ .when_some(parent_path, |then, path| {
+ then.child(Label::new(path).color(Color::Muted))
+ }),
+ ),
+ )
+ .children(jump_handler.map(|jump_handler| {
+ IconButton::new(block_id, Icon::ArrowUpRight)
+ .style(ButtonStyle::Subtle)
+ .on_click(jump_handler)
+ .tooltip(|cx| {
+ Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx)
+ })
+ })), // .p_x(gutter_padding)
+ )
} else {
let text_style = style.text.clone();
h_stack()
.id("collapsed context")
.size_full()
- .bg(gpui::red())
- .child("⋯")
- .children(jump_icon) // .p_x(gutter_padding)
+ .gap(gutter_padding)
+ .child(
+ h_stack()
+ .justify_end()
+ .flex_none()
+ .w(gutter_width - gutter_padding)
+ .h_full()
+ .text_buffer(cx)
+ .text_color(cx.theme().colors().editor_line_number)
+ .child("..."),
+ )
+ .map(|this| {
+ if let Some(jump_handler) = jump_handler {
+ this.child(
+ ButtonLike::new("jump to collapsed context")
+ .style(ButtonStyle::Transparent)
+ .full_width()
+ .on_click(jump_handler)
+ .tooltip(|cx| {
+ Tooltip::for_action(
+ "Jump to Buffer",
+ &OpenExcerpts,
+ cx,
+ )
+ })
+ .child(
+ div()
+ .h_px()
+ .w_full()
+ .bg(cx.theme().colors().border_variant)
+ .group_hover("", |style| {
+ style.bg(cx.theme().colors().border)
+ }),
+ ),
+ )
+ } else {
+ this.child(div().size_full().bg(gpui::green()))
+ }
+ })
+ // .child("⋯")
+ // .children(jump_icon) // .p_x(gutter_padding)
};
element.into_any()
}
@@ -992,10 +992,6 @@ impl Interactivity {
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
- if phase != DispatchPhase::Bubble {
- return;
- }
-
let is_hovered = interactive_bounds.visibly_contains(&event.position, cx)
&& pending_mouse_down.borrow().is_none();
if !is_hovered {
@@ -1003,6 +999,10 @@ impl Interactivity {
return;
}
+ if phase != DispatchPhase::Bubble {
+ return;
+ }
+
if active_tooltip.borrow().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
@@ -1,6 +1,7 @@
use crate::{
- Bounds, DispatchPhase, Element, ElementId, IntoElement, LayoutId, MouseDownEvent, MouseUpEvent,
- Pixels, Point, SharedString, Size, TextRun, WhiteSpace, WindowContext, WrappedLine,
+ Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
+ MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
+ WhiteSpace, WindowContext, WrappedLine,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
@@ -87,7 +88,28 @@ impl StyledText {
}
}
- pub fn with_runs(mut self, runs: Vec<TextRun>) -> Self {
+ pub fn with_highlights(
+ mut self,
+ default_style: &TextStyle,
+ highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
+ ) -> Self {
+ let mut runs = Vec::new();
+ let mut ix = 0;
+ for (range, highlight) in highlights {
+ if ix < range.start {
+ runs.push(default_style.clone().to_run(range.start - ix));
+ }
+ runs.push(
+ default_style
+ .clone()
+ .highlight(highlight)
+ .to_run(range.len()),
+ );
+ ix = range.end;
+ }
+ if ix < self.text.len() {
+ runs.push(default_style.to_run(self.text.len() - ix));
+ }
self.runs = Some(runs);
self
}
@@ -472,13 +472,27 @@ pub enum PromptLevel {
Critical,
}
+/// The style of the cursor (pointer)
#[derive(Copy, Clone, Debug)]
pub enum CursorStyle {
Arrow,
+ IBeam,
+ Crosshair,
+ ClosedHand,
+ OpenHand,
+ PointingHand,
+ ResizeLeft,
+ ResizeRight,
ResizeLeftRight,
+ ResizeUp,
+ ResizeDown,
ResizeUpDown,
- PointingHand,
- IBeam,
+ DisappearingItem,
+ IBeamCursorForVerticalLayout,
+ OperationNotAllowed,
+ DragLink,
+ DragCopy,
+ ContextualMenu,
}
impl Default for CursorStyle {
@@ -724,16 +724,35 @@ impl Platform for MacPlatform {
}
}
+ /// Match cursor style to one of the styles available
+ /// in macOS's [NSCursor](https://developer.apple.com/documentation/appkit/nscursor).
fn set_cursor_style(&self, style: CursorStyle) {
unsafe {
let new_cursor: id = match style {
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
- CursorStyle::ResizeLeftRight => {
- msg_send![class!(NSCursor), resizeLeftRightCursor]
- }
- CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
- CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
+ CursorStyle::Crosshair => msg_send![class!(NSCursor), crosshairCursor],
+ CursorStyle::ClosedHand => msg_send![class!(NSCursor), closedHandCursor],
+ CursorStyle::OpenHand => msg_send![class!(NSCursor), openHandCursor],
+ CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
+ CursorStyle::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor],
+ CursorStyle::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor],
+ CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
+ CursorStyle::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor],
+ CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
+ CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
+ CursorStyle::DisappearingItem => {
+ msg_send![class!(NSCursor), disappearingItemCursor]
+ }
+ CursorStyle::IBeamCursorForVerticalLayout => {
+ msg_send![class!(NSCursor), IBeamCursorForVerticalLayout]
+ }
+ CursorStyle::OperationNotAllowed => {
+ msg_send![class!(NSCursor), operationNotAllowedCursor]
+ }
+ CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
+ CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
+ CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor],
};
let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
@@ -238,11 +238,11 @@ impl Platform for TestPlatform {
true
}
- fn write_to_clipboard(&self, item: crate::ClipboardItem) {
+ fn write_to_clipboard(&self, item: ClipboardItem) {
*self.current_clipboard_item.lock() = Some(item);
}
- fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_clipboard(&self) -> Option<ClipboardItem> {
self.current_clipboard_item.lock().clone()
}
@@ -101,6 +101,125 @@ pub trait Styled: Sized {
self
}
+ /// Sets cursor style when hovering over an element to `text`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_text(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::IBeam);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `move`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_move(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ClosedHand);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `not-allowed`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_not_allowed(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::OperationNotAllowed);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `context-menu`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_context_menu(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ContextualMenu);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `crosshair`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_crosshair(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::Crosshair);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `vertical-text`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_vertical_text(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::IBeamCursorForVerticalLayout);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `alias`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_alias(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::DragLink);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `copy`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_copy(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::DragCopy);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `no-drop`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_no_drop(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::OperationNotAllowed);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `grab`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_grab(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::OpenHand);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `grabbing`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_grabbing(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ClosedHand);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `col-resize`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_col_resize(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ResizeLeftRight);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `row-resize`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_row_resize(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ResizeUpDown);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `n-resize`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_n_resize(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ResizeUp);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `e-resize`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_e_resize(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ResizeRight);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `s-resize`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_s_resize(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ResizeDown);
+ self
+ }
+
+ /// Sets cursor style when hovering over an element to `w-resize`.
+ /// [Docs](https://tailwindcss.com/docs/cursor)
+ fn cursor_w_resize(mut self) -> Self {
+ self.style().mouse_cursor = Some(CursorStyle::ResizeLeft);
+ self
+ }
+
/// Sets the whitespace of the element to `normal`.
/// [Docs](https://tailwindcss.com/docs/whitespace#normal)
fn whitespace_normal(mut self) -> Self {
@@ -0,0 +1,26 @@
+[package]
+name = "language_selector2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/language_selector.rs"
+doctest = false
+
+[dependencies]
+editor = { package = "editor2", path = "../editor2" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
+language = { package = "language2", path = "../language2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+picker = { package = "picker2", path = "../picker2" }
+project = { package = "project2", path = "../project2" }
+theme = { package = "theme2", path = "../theme2" }
+ui = { package = "ui2", path = "../ui2" }
+settings = { package = "settings2", path = "../settings2" }
+util = { path = "../util" }
+workspace = { package = "workspace2", path = "../workspace2" }
+anyhow.workspace = true
+
+[dev-dependencies]
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
@@ -0,0 +1,82 @@
+use editor::Editor;
+use gpui::{
+ div, Div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView,
+};
+use std::sync::Arc;
+use ui::{Button, ButtonCommon, Clickable, Tooltip};
+use workspace::{item::ItemHandle, StatusItemView, Workspace};
+
+use crate::LanguageSelector;
+
+pub struct ActiveBufferLanguage {
+ active_language: Option<Option<Arc<str>>>,
+ workspace: WeakView<Workspace>,
+ _observe_active_editor: Option<Subscription>,
+}
+
+impl ActiveBufferLanguage {
+ pub fn new(workspace: &Workspace) -> Self {
+ Self {
+ active_language: None,
+ workspace: workspace.weak_handle(),
+ _observe_active_editor: None,
+ }
+ }
+
+ fn update_language(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
+ self.active_language = Some(None);
+
+ let editor = editor.read(cx);
+ if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
+ if let Some(language) = buffer.read(cx).language() {
+ self.active_language = Some(Some(language.name()));
+ }
+ }
+
+ cx.notify();
+ }
+}
+
+impl Render for ActiveBufferLanguage {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Div {
+ div().when_some(self.active_language.as_ref(), |el, active_language| {
+ let active_language_text = if let Some(active_language_text) = active_language {
+ active_language_text.to_string()
+ } else {
+ "Unknown".to_string()
+ };
+
+ el.child(
+ Button::new("change-language", active_language_text)
+ .on_click(cx.listener(|this, _, cx| {
+ if let Some(workspace) = this.workspace.upgrade() {
+ workspace.update(cx, |workspace, cx| {
+ LanguageSelector::toggle(workspace, cx)
+ });
+ }
+ }))
+ .tooltip(|cx| Tooltip::text("Select Language", cx)),
+ )
+ })
+ }
+}
+
+impl StatusItemView for ActiveBufferLanguage {
+ fn set_active_pane_item(
+ &mut self,
+ active_pane_item: Option<&dyn ItemHandle>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ if let Some(editor) = active_pane_item.and_then(|item| item.act_as::<Editor>(cx)) {
+ self._observe_active_editor = Some(cx.observe(&editor, Self::update_language));
+ self.update_language(editor, cx);
+ } else {
+ self.active_language = None;
+ self._observe_active_editor = None;
+ }
+
+ cx.notify();
+ }
+}
@@ -0,0 +1,231 @@
+mod active_buffer_language;
+
+pub use active_buffer_language::ActiveBufferLanguage;
+use anyhow::anyhow;
+use editor::Editor;
+use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
+use gpui::{
+ actions, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Model,
+ ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
+};
+use language::{Buffer, LanguageRegistry};
+use picker::{Picker, PickerDelegate};
+use project::Project;
+use std::sync::Arc;
+use ui::{v_stack, HighlightedLabel, ListItem, Selectable};
+use util::ResultExt;
+use workspace::Workspace;
+
+actions!(Toggle);
+
+pub fn init(cx: &mut AppContext) {
+ cx.observe_new_views(LanguageSelector::register).detach();
+}
+
+pub struct LanguageSelector {
+ picker: View<Picker<LanguageSelectorDelegate>>,
+}
+
+impl LanguageSelector {
+ fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+ workspace.register_action(move |workspace, _: &Toggle, cx| {
+ Self::toggle(workspace, cx);
+ });
+ }
+
+ fn toggle(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<()> {
+ let registry = workspace.app_state().languages.clone();
+ let (_, buffer, _) = workspace
+ .active_item(cx)?
+ .act_as::<Editor>(cx)?
+ .read(cx)
+ .active_excerpt(cx)?;
+ let project = workspace.project().clone();
+
+ workspace.toggle_modal(cx, move |cx| {
+ LanguageSelector::new(buffer, project, registry, cx)
+ });
+ Some(())
+ }
+
+ fn new(
+ buffer: Model<Buffer>,
+ project: Model<Project>,
+ language_registry: Arc<LanguageRegistry>,
+ cx: &mut ViewContext<Self>,
+ ) -> Self {
+ let delegate = LanguageSelectorDelegate::new(
+ cx.view().downgrade(),
+ buffer,
+ project,
+ language_registry,
+ );
+
+ let picker = cx.build_view(|cx| Picker::new(delegate, cx));
+ Self { picker }
+ }
+}
+
+impl Render for LanguageSelector {
+ type Element = Div;
+
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+ v_stack().min_w_96().child(self.picker.clone())
+ }
+}
+
+impl FocusableView for LanguageSelector {
+ fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+ self.picker.focus_handle(cx)
+ }
+}
+impl EventEmitter<DismissEvent> for LanguageSelector {}
+
+pub struct LanguageSelectorDelegate {
+ language_selector: WeakView<LanguageSelector>,
+ buffer: Model<Buffer>,
+ project: Model<Project>,
+ language_registry: Arc<LanguageRegistry>,
+ candidates: Vec<StringMatchCandidate>,
+ matches: Vec<StringMatch>,
+ selected_index: usize,
+}
+
+impl LanguageSelectorDelegate {
+ fn new(
+ language_selector: WeakView<LanguageSelector>,
+ buffer: Model<Buffer>,
+ project: Model<Project>,
+ language_registry: Arc<LanguageRegistry>,
+ ) -> Self {
+ let candidates = language_registry
+ .language_names()
+ .into_iter()
+ .enumerate()
+ .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name))
+ .collect::<Vec<_>>();
+
+ Self {
+ language_selector,
+ buffer,
+ project,
+ language_registry,
+ candidates,
+ matches: vec![],
+ selected_index: 0,
+ }
+ }
+}
+
+impl PickerDelegate for LanguageSelectorDelegate {
+ type ListItem = ListItem;
+
+ fn placeholder_text(&self) -> Arc<str> {
+ "Select a language...".into()
+ }
+
+ fn match_count(&self) -> usize {
+ self.matches.len()
+ }
+
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+ if let Some(mat) = self.matches.get(self.selected_index) {
+ let language_name = &self.candidates[mat.candidate_id].string;
+ let language = self.language_registry.language_for_name(language_name);
+ let project = self.project.downgrade();
+ let buffer = self.buffer.downgrade();
+ cx.spawn(|_, mut cx| async move {
+ let language = language.await?;
+ let project = project
+ .upgrade()
+ .ok_or_else(|| anyhow!("project was dropped"))?;
+ let buffer = buffer
+ .upgrade()
+ .ok_or_else(|| anyhow!("buffer was dropped"))?;
+ project.update(&mut cx, |project, cx| {
+ project.set_language_for_buffer(&buffer, language, cx);
+ })
+ })
+ .detach_and_log_err(cx);
+ }
+ self.dismissed(cx);
+ }
+
+ fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+ self.language_selector
+ .update(cx, |_, cx| cx.emit(DismissEvent))
+ .log_err();
+ }
+
+ fn selected_index(&self) -> usize {
+ self.selected_index
+ }
+
+ fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
+ self.selected_index = ix;
+ }
+
+ fn update_matches(
+ &mut self,
+ query: String,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> gpui::Task<()> {
+ let background = cx.background_executor().clone();
+ let candidates = self.candidates.clone();
+ cx.spawn(|this, mut cx| async move {
+ let matches = if query.is_empty() {
+ candidates
+ .into_iter()
+ .enumerate()
+ .map(|(index, candidate)| StringMatch {
+ candidate_id: index,
+ string: candidate.string,
+ positions: Vec::new(),
+ score: 0.0,
+ })
+ .collect()
+ } else {
+ match_strings(
+ &candidates,
+ &query,
+ false,
+ 100,
+ &Default::default(),
+ background,
+ )
+ .await
+ };
+
+ this.update(&mut cx, |this, cx| {
+ let delegate = &mut this.delegate;
+ delegate.matches = matches;
+ delegate.selected_index = delegate
+ .selected_index
+ .min(delegate.matches.len().saturating_sub(1));
+ cx.notify();
+ })
+ .log_err();
+ })
+ }
+
+ fn render_match(
+ &self,
+ ix: usize,
+ selected: bool,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> Option<Self::ListItem> {
+ let mat = &self.matches[ix];
+ let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name());
+ let mut label = mat.string.clone();
+ if buffer_language_name.as_deref() == Some(mat.string.as_str()) {
+ label.push_str(" (current)");
+ }
+
+ Some(
+ ListItem::new(ix)
+ .inset(true)
+ .selected(selected)
+ .child(HighlightedLabel::new(label, mat.positions.clone())),
+ )
+ }
+}
@@ -178,6 +178,15 @@ impl<D: PickerDelegate> Picker<D> {
}
cx.notify();
}
+
+ pub fn query(&self, cx: &AppContext) -> String {
+ self.editor.read(cx).text(cx)
+ }
+
+ pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
+ self.editor
+ .update(cx, |editor, cx| editor.set_text(query, cx));
+ }
}
impl<D: PickerDelegate> Render for Picker<D> {
@@ -1661,14 +1661,15 @@ impl Project {
path: impl Into<ProjectPath>,
cx: &mut ModelContext<Self>,
) -> Task<Result<(ProjectEntryId, AnyModelHandle)>> {
- let task = self.open_buffer(path, cx);
+ let project_path = path.into();
+ let task = self.open_buffer(project_path.clone(), cx);
cx.spawn_weak(|_, cx| async move {
let buffer = task.await?;
let project_entry_id = buffer
.read_with(&cx, |buffer, cx| {
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
})
- .ok_or_else(|| anyhow!("no project entry"))?;
+ .with_context(|| format!("no project entry for {project_path:?}"))?;
let buffer: &AnyModelHandle = &buffer;
Ok((project_entry_id, buffer.clone()))
@@ -1691,14 +1691,15 @@ impl Project {
path: impl Into<ProjectPath>,
cx: &mut ModelContext<Self>,
) -> Task<Result<(ProjectEntryId, AnyModel)>> {
- let task = self.open_buffer(path, cx);
+ let project_path = path.into();
+ let task = self.open_buffer(project_path.clone(), cx);
cx.spawn(move |_, mut cx| async move {
let buffer = task.await?;
let project_entry_id = buffer
.update(&mut cx, |buffer, cx| {
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
})?
- .ok_or_else(|| anyhow!("no project entry"))?;
+ .with_context(|| format!("no project entry for {project_path:?}"))?;
let buffer: &AnyModel = &buffer;
Ok((project_entry_id, buffer.clone()))
@@ -9,4 +9,4 @@ pub use notification::*;
pub use peer::*;
mod macros;
-pub const PROTOCOL_VERSION: u32 = 64;
+pub const PROTOCOL_VERSION: u32 = 66;
@@ -1,4 +1,5 @@
mod auto_height_editor;
+mod cursor;
mod focus;
mod kitchen_sink;
mod picker;
@@ -7,6 +8,7 @@ mod text;
mod z_index;
pub use auto_height_editor::*;
+pub use cursor::*;
pub use focus::*;
pub use kitchen_sink::*;
pub use picker::*;
@@ -0,0 +1,112 @@
+use gpui::{Div, Render, Stateful};
+use story::Story;
+use ui::prelude::*;
+
+pub struct CursorStory;
+
+impl Render for CursorStory {
+ type Element = Div;
+
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+ let all_cursors: [(&str, Box<dyn Fn(Stateful<Div>) -> Stateful<Div>>); 19] = [
+ (
+ "cursor_default",
+ Box::new(|el: Stateful<Div>| el.cursor_default()),
+ ),
+ (
+ "cursor_pointer",
+ Box::new(|el: Stateful<Div>| el.cursor_pointer()),
+ ),
+ (
+ "cursor_text",
+ Box::new(|el: Stateful<Div>| el.cursor_text()),
+ ),
+ (
+ "cursor_move",
+ Box::new(|el: Stateful<Div>| el.cursor_move()),
+ ),
+ (
+ "cursor_not_allowed",
+ Box::new(|el: Stateful<Div>| el.cursor_not_allowed()),
+ ),
+ (
+ "cursor_context_menu",
+ Box::new(|el: Stateful<Div>| el.cursor_context_menu()),
+ ),
+ (
+ "cursor_crosshair",
+ Box::new(|el: Stateful<Div>| el.cursor_crosshair()),
+ ),
+ (
+ "cursor_vertical_text",
+ Box::new(|el: Stateful<Div>| el.cursor_vertical_text()),
+ ),
+ (
+ "cursor_alias",
+ Box::new(|el: Stateful<Div>| el.cursor_alias()),
+ ),
+ (
+ "cursor_copy",
+ Box::new(|el: Stateful<Div>| el.cursor_copy()),
+ ),
+ (
+ "cursor_no_drop",
+ Box::new(|el: Stateful<Div>| el.cursor_no_drop()),
+ ),
+ (
+ "cursor_grab",
+ Box::new(|el: Stateful<Div>| el.cursor_grab()),
+ ),
+ (
+ "cursor_grabbing",
+ Box::new(|el: Stateful<Div>| el.cursor_grabbing()),
+ ),
+ (
+ "cursor_col_resize",
+ Box::new(|el: Stateful<Div>| el.cursor_col_resize()),
+ ),
+ (
+ "cursor_row_resize",
+ Box::new(|el: Stateful<Div>| el.cursor_row_resize()),
+ ),
+ (
+ "cursor_n_resize",
+ Box::new(|el: Stateful<Div>| el.cursor_n_resize()),
+ ),
+ (
+ "cursor_e_resize",
+ Box::new(|el: Stateful<Div>| el.cursor_e_resize()),
+ ),
+ (
+ "cursor_s_resize",
+ Box::new(|el: Stateful<Div>| el.cursor_s_resize()),
+ ),
+ (
+ "cursor_w_resize",
+ Box::new(|el: Stateful<Div>| el.cursor_w_resize()),
+ ),
+ ];
+
+ Story::container()
+ .flex()
+ .gap_1()
+ .child(Story::title("cursor"))
+ .children(all_cursors.map(|(name, apply_cursor)| {
+ div().gap_1().flex().text_color(gpui::white()).child(
+ div()
+ .flex()
+ .items_center()
+ .justify_center()
+ .id(name)
+ .map(apply_cursor)
+ .w_64()
+ .h_8()
+ .bg(gpui::red())
+ .hover(|style| style.bg(gpui::blue()))
+ .active(|style| style.bg(gpui::green()))
+ .text_sm()
+ .child(Story::label(name)),
+ )
+ }))
+ }
+}
@@ -1,6 +1,6 @@
use gpui::{
- blue, div, green, red, white, Div, InteractiveText, ParentElement, Render, Styled, StyledText,
- TextRun, View, VisualContext, WindowContext,
+ blue, div, green, red, white, Div, HighlightStyle, InteractiveText, ParentElement, Render,
+ Styled, StyledText, View, VisualContext, WindowContext,
};
use ui::v_stack;
@@ -59,13 +59,11 @@ impl Render for TextStory {
))).child(
InteractiveText::new(
"interactive",
- StyledText::new("Hello world, how is it going?").with_runs(vec![
- cx.text_style().to_run(6),
- TextRun {
+ StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
+ (6..11, HighlightStyle {
background_color: Some(green()),
- ..cx.text_style().to_run(5)
- },
- cx.text_style().to_run(18),
+ ..Default::default()
+ }),
]),
)
.on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
@@ -17,6 +17,7 @@ pub enum ComponentStory {
Button,
Checkbox,
ContextMenu,
+ Cursor,
Disclosure,
Focus,
Icon,
@@ -40,6 +41,7 @@ impl ComponentStory {
Self::Button => cx.build_view(|_| ui::ButtonStory).into(),
Self::Checkbox => cx.build_view(|_| ui::CheckboxStory).into(),
Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into(),
+ Self::Cursor => cx.build_view(|_| crate::stories::CursorStory).into(),
Self::Disclosure => cx.build_view(|_| ui::DisclosureStory).into(),
Self::Focus => FocusStory::view(cx).into(),
Self::Icon => cx.build_view(|_| ui::IconStory).into(),
@@ -52,13 +52,13 @@ pub(crate) fn one_dark() -> Theme {
element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0),
element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
- element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
+ element_disabled: SystemColors::default().transparent,
drop_target_background: hsla(220.0 / 360., 8.3 / 100., 21.4 / 100., 1.0),
ghost_element_background: SystemColors::default().transparent,
ghost_element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
ghost_element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0),
ghost_element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
- ghost_element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
+ ghost_element_disabled: SystemColors::default().transparent,
text: hsla(221. / 360., 11. / 100., 86. / 100., 1.0),
text_muted: hsla(218.0 / 360., 7. / 100., 46. / 100., 1.0),
text_placeholder: hsla(220.0 / 360., 6.6 / 100., 44.5 / 100., 1.0),
@@ -9,6 +9,8 @@ mod keybinding;
mod label;
mod list;
mod popover;
+mod popover_menu;
+mod right_click_menu;
mod stack;
mod tooltip;
@@ -26,6 +28,8 @@ pub use keybinding::*;
pub use label::*;
pub use list::*;
pub use popover::*;
+pub use popover_menu::*;
+pub use right_click_menu::*;
pub use stack::*;
pub use tooltip::*;
@@ -1,4 +1,5 @@
mod button;
+pub(self) mod button_icon;
mod button_like;
mod icon_button;
@@ -1,13 +1,22 @@
-use gpui::AnyView;
+use gpui::{AnyView, DefiniteLength};
use crate::prelude::*;
-use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Label, LineHeightStyle};
+use crate::{
+ ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize, Label, LineHeightStyle,
+};
+
+use super::button_icon::ButtonIcon;
#[derive(IntoElement)]
pub struct Button {
base: ButtonLike,
label: SharedString,
label_color: Option<Color>,
+ selected_label: Option<SharedString>,
+ icon: Option<Icon>,
+ icon_size: Option<IconSize>,
+ icon_color: Option<Color>,
+ selected_icon: Option<Icon>,
}
impl Button {
@@ -16,6 +25,11 @@ impl Button {
base: ButtonLike::new(id),
label: label.into(),
label_color: None,
+ selected_label: None,
+ icon: None,
+ icon_size: None,
+ icon_color: None,
+ selected_icon: None,
}
}
@@ -23,6 +37,31 @@ impl Button {
self.label_color = label_color.into();
self
}
+
+ pub fn selected_label<L: Into<SharedString>>(mut self, label: impl Into<Option<L>>) -> Self {
+ self.selected_label = label.into().map(Into::into);
+ self
+ }
+
+ pub fn icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
+ self.icon = icon.into();
+ self
+ }
+
+ pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
+ self.icon_size = icon_size.into();
+ self
+ }
+
+ pub fn icon_color(mut self, icon_color: impl Into<Option<Color>>) -> Self {
+ self.icon_color = icon_color.into();
+ self
+ }
+
+ pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
+ self.selected_icon = icon.into();
+ self
+ }
}
impl Selectable for Button {
@@ -49,6 +88,18 @@ impl Clickable for Button {
}
}
+impl FixedWidth for Button {
+ fn width(mut self, width: DefiniteLength) -> Self {
+ self.base = self.base.width(width);
+ self
+ }
+
+ fn full_width(mut self) -> Self {
+ self.base = self.base.full_width();
+ self
+ }
+}
+
impl ButtonCommon for Button {
fn id(&self) -> &ElementId {
self.base.id()
@@ -74,18 +125,35 @@ impl RenderOnce for Button {
type Rendered = ButtonLike;
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
- let label_color = if self.base.disabled {
+ let is_disabled = self.base.disabled;
+ let is_selected = self.base.selected;
+
+ let label = self
+ .selected_label
+ .filter(|_| is_selected)
+ .unwrap_or(self.label);
+
+ let label_color = if is_disabled {
Color::Disabled
- } else if self.base.selected {
+ } else if is_selected {
Color::Selected
} else {
self.label_color.unwrap_or_default()
};
- self.base.child(
- Label::new(self.label)
- .color(label_color)
- .line_height_style(LineHeightStyle::UILabel),
- )
+ self.base
+ .children(self.icon.map(|icon| {
+ ButtonIcon::new(icon)
+ .disabled(is_disabled)
+ .selected(is_selected)
+ .selected_icon(self.selected_icon)
+ .size(self.icon_size)
+ .color(self.icon_color)
+ }))
+ .child(
+ Label::new(label)
+ .color(label_color)
+ .line_height_style(LineHeightStyle::UILabel),
+ )
}
}
@@ -0,0 +1,84 @@
+use crate::{prelude::*, Icon, IconElement, IconSize};
+
+/// An icon that appears within a button.
+///
+/// Can be used as either an icon alongside a label, like in [`Button`](crate::Button),
+/// or as a standalone icon, like in [`IconButton`](crate::IconButton).
+#[derive(IntoElement)]
+pub(super) struct ButtonIcon {
+ icon: Icon,
+ size: IconSize,
+ color: Color,
+ disabled: bool,
+ selected: bool,
+ selected_icon: Option<Icon>,
+}
+
+impl ButtonIcon {
+ pub fn new(icon: Icon) -> Self {
+ Self {
+ icon,
+ size: IconSize::default(),
+ color: Color::default(),
+ disabled: false,
+ selected: false,
+ selected_icon: None,
+ }
+ }
+
+ pub fn size(mut self, size: impl Into<Option<IconSize>>) -> Self {
+ if let Some(size) = size.into() {
+ self.size = size;
+ }
+
+ self
+ }
+
+ pub fn color(mut self, color: impl Into<Option<Color>>) -> Self {
+ if let Some(color) = color.into() {
+ self.color = color;
+ }
+
+ self
+ }
+
+ pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
+ self.selected_icon = icon.into();
+ self
+ }
+}
+
+impl Disableable for ButtonIcon {
+ fn disabled(mut self, disabled: bool) -> Self {
+ self.disabled = disabled;
+ self
+ }
+}
+
+impl Selectable for ButtonIcon {
+ fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
+ self
+ }
+}
+
+impl RenderOnce for ButtonIcon {
+ type Rendered = IconElement;
+
+ fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
+ let icon = self
+ .selected_icon
+ .filter(|_| self.selected)
+ .unwrap_or(self.icon);
+
+ let icon_color = if self.disabled {
+ Color::Disabled
+ } else if self.selected {
+ Color::Selected
+ } else {
+ self.color
+ };
+
+ IconElement::new(icon).size(self.size).color(icon_color)
+ }
+}
@@ -1,3 +1,4 @@
+use gpui::{relative, DefiniteLength};
use gpui::{rems, transparent_black, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful};
use smallvec::SmallVec;
@@ -5,18 +6,50 @@ use crate::h_stack;
use crate::prelude::*;
pub trait ButtonCommon: Clickable + Disableable {
+ /// A unique element ID to identify the button.
fn id(&self) -> &ElementId;
+
+ /// The visual style of the button.
+ ///
+ /// Mosty commonly will be [`ButtonStyle::Subtle`], or [`ButtonStyle::Filled`]
+ /// for an emphasized button.
fn style(self, style: ButtonStyle) -> Self;
+
+ /// The size of the button.
+ ///
+ /// Most buttons will use the default size.
+ ///
+ /// [`ButtonSize`] can also be used to help build non-button elements
+ /// that are consistently sized with buttons.
fn size(self, size: ButtonSize) -> Self;
+
+ /// The tooltip that shows when a user hovers over the button.
+ ///
+ /// Nearly all interactable elements should have a tooltip. Some example
+ /// exceptions might a scroll bar, or a slider.
fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum ButtonStyle {
- #[default]
+ /// A filled button with a solid background color. Provides emphasis versus
+ /// the more common subtle button.
Filled,
- // Tinted,
+
+ /// 🚧 Under construction 🚧
+ ///
+ /// Used to emphasize a button in some way, like a selected state, or a semantic
+ /// coloring like an error or success button.
+ Tinted,
+
+ /// The default button style, used for most buttons. Has a transparent background,
+ /// but has a background color to indicate states like hover and active.
+ #[default]
Subtle,
+
+ /// Used for buttons that only change forground color on hover and active states.
+ ///
+ /// TODO: Better docs for this.
Transparent,
}
@@ -40,6 +73,12 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
+ ButtonStyle::Tinted => ButtonLikeStyles {
+ background: gpui::red(),
+ border_color: gpui::red(),
+ label_color: gpui::red(),
+ icon_color: gpui::red(),
+ },
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_background,
border_color: transparent_black(),
@@ -63,6 +102,12 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
+ ButtonStyle::Tinted => ButtonLikeStyles {
+ background: gpui::red(),
+ border_color: gpui::red(),
+ label_color: gpui::red(),
+ icon_color: gpui::red(),
+ },
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_hover,
border_color: transparent_black(),
@@ -88,6 +133,12 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
+ ButtonStyle::Tinted => ButtonLikeStyles {
+ background: gpui::red(),
+ border_color: gpui::red(),
+ label_color: gpui::red(),
+ icon_color: gpui::red(),
+ },
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_active,
border_color: transparent_black(),
@@ -114,6 +165,12 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
+ ButtonStyle::Tinted => ButtonLikeStyles {
+ background: gpui::red(),
+ border_color: gpui::red(),
+ label_color: gpui::red(),
+ icon_color: gpui::red(),
+ },
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_background,
border_color: cx.theme().colors().border_focused,
@@ -137,6 +194,12 @@ impl ButtonStyle {
label_color: Color::Disabled.color(cx),
icon_color: Color::Disabled.color(cx),
},
+ ButtonStyle::Tinted => ButtonLikeStyles {
+ background: gpui::red(),
+ border_color: gpui::red(),
+ label_color: gpui::red(),
+ icon_color: gpui::red(),
+ },
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_disabled,
border_color: cx.theme().colors().border_disabled,
@@ -153,6 +216,8 @@ impl ButtonStyle {
}
}
+/// ButtonSize can also be used to help build non-button elements
+/// that are consistently sized with buttons.
#[derive(Default, PartialEq, Clone, Copy)]
pub enum ButtonSize {
#[default]
@@ -171,12 +236,18 @@ impl ButtonSize {
}
}
+/// A button-like element that can be used to create a custom button when
+/// prebuilt buttons are not sufficient. Use this sparingly, as it is
+/// unconstrained and may make the UI feel less consistent.
+///
+/// This is also used to build the prebuilt buttons.
#[derive(IntoElement)]
pub struct ButtonLike {
id: ElementId,
pub(super) style: ButtonStyle,
pub(super) disabled: bool,
pub(super) selected: bool,
+ pub(super) width: Option<DefiniteLength>,
size: ButtonSize,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
@@ -190,6 +261,7 @@ impl ButtonLike {
style: ButtonStyle::default(),
disabled: false,
selected: false,
+ width: None,
size: ButtonSize::Default,
tooltip: None,
children: SmallVec::new(),
@@ -219,6 +291,18 @@ impl Clickable for ButtonLike {
}
}
+impl FixedWidth for ButtonLike {
+ fn width(mut self, width: DefiniteLength) -> Self {
+ self.width = Some(width);
+ self
+ }
+
+ fn full_width(mut self) -> Self {
+ self.width = Some(relative(1.));
+ self
+ }
+}
+
impl ButtonCommon for ButtonLike {
fn id(&self) -> &ElementId {
&self.id
@@ -252,14 +336,19 @@ impl RenderOnce for ButtonLike {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
h_stack()
.id(self.id.clone())
+ .group("")
+ .flex_none()
.h(self.size.height())
+ .when_some(self.width, |this, width| this.w(width))
.rounded_md()
- .cursor_pointer()
.gap_1()
.px_1()
.bg(self.style.enabled(cx).background)
- .hover(|hover| hover.bg(self.style.hovered(cx).background))
- .active(|active| active.bg(self.style.active(cx).background))
+ .when(!self.disabled, |this| {
+ this.cursor_pointer()
+ .hover(|hover| hover.bg(self.style.hovered(cx).background))
+ .active(|active| active.bg(self.style.active(cx).background))
+ })
.when_some(
self.on_click.filter(|_| !self.disabled),
|this, on_click| {
@@ -270,7 +359,11 @@ impl RenderOnce for ButtonLike {
},
)
.when_some(self.tooltip, |this, tooltip| {
- this.tooltip(move |cx| tooltip(cx))
+ if !self.selected {
+ this.tooltip(move |cx| tooltip(cx))
+ } else {
+ this
+ }
})
.children(self.children)
}
@@ -1,7 +1,9 @@
-use gpui::{Action, AnyView};
+use gpui::{Action, AnyView, DefiniteLength};
use crate::prelude::*;
-use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconElement, IconSize};
+use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize};
+
+use super::button_icon::ButtonIcon;
#[derive(IntoElement)]
pub struct IconButton {
@@ -9,6 +11,7 @@ pub struct IconButton {
icon: Icon,
icon_size: IconSize,
icon_color: Color,
+ selected_icon: Option<Icon>,
}
impl IconButton {
@@ -18,6 +21,7 @@ impl IconButton {
icon,
icon_size: IconSize::default(),
icon_color: Color::Default,
+ selected_icon: None,
}
}
@@ -31,6 +35,11 @@ impl IconButton {
self
}
+ pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
+ self.selected_icon = icon.into();
+ self
+ }
+
pub fn action(self, action: Box<dyn Action>) -> Self {
self.on_click(move |_event, cx| cx.dispatch_action(action.boxed_clone()))
}
@@ -60,6 +69,18 @@ impl Clickable for IconButton {
}
}
+impl FixedWidth for IconButton {
+ fn width(mut self, width: DefiniteLength) -> Self {
+ self.base = self.base.width(width);
+ self
+ }
+
+ fn full_width(mut self) -> Self {
+ self.base = self.base.full_width();
+ self
+ }
+}
+
impl ButtonCommon for IconButton {
fn id(&self) -> &ElementId {
self.base.id()
@@ -85,18 +106,16 @@ impl RenderOnce for IconButton {
type Rendered = ButtonLike;
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
- let icon_color = if self.base.disabled {
- Color::Disabled
- } else if self.base.selected {
- Color::Selected
- } else {
- self.icon_color
- };
+ let is_disabled = self.base.disabled;
+ let is_selected = self.base.selected;
self.base.child(
- IconElement::new(self.icon)
+ ButtonIcon::new(self.icon)
+ .disabled(is_disabled)
+ .selected(is_selected)
+ .selected_icon(self.selected_icon)
.size(self.icon_size)
- .color(icon_color),
+ .color(self.icon_color),
)
}
}
@@ -1,19 +1,20 @@
use crate::{
- h_stack, prelude::*, v_stack, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader,
+ h_stack, prelude::*, v_stack, Icon, IconElement, KeyBinding, Label, List, ListItem,
+ ListSeparator, ListSubHeader,
};
use gpui::{
- overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, DismissEvent, DispatchPhase,
- Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId, ManagedView, MouseButton,
- MouseDownEvent, Pixels, Point, Render, View, VisualContext,
+ px, Action, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
+ IntoElement, Render, View, VisualContext,
};
use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
-use std::{cell::RefCell, rc::Rc};
+use std::rc::Rc;
pub enum ContextMenuItem {
Separator,
Header(SharedString),
Entry {
label: SharedString,
+ icon: Option<Icon>,
handler: Rc<dyn Fn(&mut WindowContext)>,
key_binding: Option<KeyBinding>,
},
@@ -70,6 +71,7 @@ impl ContextMenu {
label: label.into(),
handler: Rc::new(on_click),
key_binding: None,
+ icon: None,
});
self
}
@@ -84,6 +86,22 @@ impl ContextMenu {
label: label.into(),
key_binding: KeyBinding::for_action(&*action, cx),
handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
+ icon: None,
+ });
+ self
+ }
+
+ pub fn link(
+ mut self,
+ label: impl Into<SharedString>,
+ action: Box<dyn Action>,
+ cx: &mut WindowContext,
+ ) -> Self {
+ self.items.push(ContextMenuItem::Entry {
+ label: label.into(),
+ key_binding: KeyBinding::for_action(&*action, cx),
+ handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
+ icon: Some(Icon::Link),
});
self
}
@@ -176,19 +194,30 @@ impl Render for ContextMenu {
ListSubHeader::new(header.clone()).into_any_element()
}
ContextMenuItem::Entry {
- label: entry,
- handler: callback,
+ label,
+ handler,
key_binding,
+ icon,
} => {
- let callback = callback.clone();
+ let handler = handler.clone();
let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent));
- ListItem::new(entry.clone())
+ let label_element = if let Some(icon) = icon {
+ h_stack()
+ .gap_1()
+ .child(Label::new(label.clone()))
+ .child(IconElement::new(*icon))
+ .into_any_element()
+ } else {
+ Label::new(label.clone()).into_any_element()
+ };
+
+ ListItem::new(label.clone())
.child(
h_stack()
.w_full()
.justify_between()
- .child(Label::new(entry.clone()))
+ .child(label_element)
.children(
key_binding
.clone()
@@ -197,7 +226,7 @@ impl Render for ContextMenu {
)
.selected(Some(ix) == self.selected_index)
.on_click(move |event, cx| {
- callback(cx);
+ handler(cx);
dismiss(event, cx)
})
.into_any_element()
@@ -208,174 +237,3 @@ impl Render for ContextMenu {
)
}
}
-
-pub struct MenuHandle<M: ManagedView> {
- id: ElementId,
- child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement + 'static>>,
- menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
- anchor: Option<AnchorCorner>,
- attach: Option<AnchorCorner>,
-}
-
-impl<M: ManagedView> MenuHandle<M> {
- pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> View<M> + 'static) -> Self {
- self.menu_builder = Some(Rc::new(f));
- self
- }
-
- pub fn child<R: IntoElement>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
- self.child_builder = Some(Box::new(|b| f(b).into_element().into_any()));
- self
- }
-
- /// anchor defines which corner of the menu to anchor to the attachment point
- /// (by default the cursor position, but see attach)
- pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
- self.anchor = Some(anchor);
- self
- }
-
- /// attach defines which corner of the handle to attach the menu's anchor to
- pub fn attach(mut self, attach: AnchorCorner) -> Self {
- self.attach = Some(attach);
- self
- }
-}
-
-pub fn menu_handle<M: ManagedView>(id: impl Into<ElementId>) -> MenuHandle<M> {
- MenuHandle {
- id: id.into(),
- child_builder: None,
- menu_builder: None,
- anchor: None,
- attach: None,
- }
-}
-
-pub struct MenuHandleState<M> {
- menu: Rc<RefCell<Option<View<M>>>>,
- position: Rc<RefCell<Point<Pixels>>>,
- child_layout_id: Option<LayoutId>,
- child_element: Option<AnyElement>,
- menu_element: Option<AnyElement>,
-}
-
-impl<M: ManagedView> Element for MenuHandle<M> {
- type State = MenuHandleState<M>;
-
- fn layout(
- &mut self,
- element_state: Option<Self::State>,
- cx: &mut WindowContext,
- ) -> (gpui::LayoutId, Self::State) {
- let (menu, position) = if let Some(element_state) = element_state {
- (element_state.menu, element_state.position)
- } else {
- (Rc::default(), Rc::default())
- };
-
- let mut menu_layout_id = None;
-
- let menu_element = menu.borrow_mut().as_mut().map(|menu| {
- let mut overlay = overlay().snap_to_window();
- if let Some(anchor) = self.anchor {
- overlay = overlay.anchor(anchor);
- }
- overlay = overlay.position(*position.borrow());
-
- let mut element = overlay.child(menu.clone()).into_any();
- menu_layout_id = Some(element.layout(cx));
- element
- });
-
- let mut child_element = self
- .child_builder
- .take()
- .map(|child_builder| (child_builder)(menu.borrow().is_some()));
-
- let child_layout_id = child_element
- .as_mut()
- .map(|child_element| child_element.layout(cx));
-
- let layout_id = cx.request_layout(
- &gpui::Style::default(),
- menu_layout_id.into_iter().chain(child_layout_id),
- );
-
- (
- layout_id,
- MenuHandleState {
- menu,
- position,
- child_element,
- child_layout_id,
- menu_element,
- },
- )
- }
-
- fn paint(
- self,
- bounds: Bounds<gpui::Pixels>,
- element_state: &mut Self::State,
- cx: &mut WindowContext,
- ) {
- if let Some(child) = element_state.child_element.take() {
- child.paint(cx);
- }
-
- if let Some(menu) = element_state.menu_element.take() {
- menu.paint(cx);
- return;
- }
-
- let Some(builder) = self.menu_builder else {
- return;
- };
- let menu = element_state.menu.clone();
- let position = element_state.position.clone();
- let attach = self.attach.clone();
- let child_layout_id = element_state.child_layout_id.clone();
-
- cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
- if phase == DispatchPhase::Bubble
- && event.button == MouseButton::Right
- && bounds.contains_point(&event.position)
- {
- cx.stop_propagation();
- cx.prevent_default();
-
- let new_menu = (builder)(cx);
- let menu2 = menu.clone();
- cx.subscribe(&new_menu, move |_modal, _: &DismissEvent, cx| {
- *menu2.borrow_mut() = None;
- cx.notify();
- })
- .detach();
- cx.focus_view(&new_menu);
- *menu.borrow_mut() = Some(new_menu);
-
- *position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() {
- attach
- .unwrap()
- .corner(cx.layout_bounds(child_layout_id.unwrap()))
- } else {
- cx.mouse_position()
- };
- cx.notify();
- }
- });
- }
-}
-
-impl<M: ManagedView> IntoElement for MenuHandle<M> {
- type Element = Self;
-
- fn element_id(&self) -> Option<gpui::ElementId> {
- Some(self.id.clone())
- }
-
- fn into_element(self) -> Self::Element {
- self
- }
-}
@@ -27,6 +27,7 @@ pub enum Icon {
Bolt,
CaseSensitive,
Check,
+ Copy,
ChevronDown,
ChevronLeft,
ChevronRight,
@@ -54,6 +55,7 @@ pub enum Icon {
FolderX,
Hash,
InlayHint,
+ Link,
MagicWand,
MagnifyingGlass,
MailOpen,
@@ -99,6 +101,7 @@ impl Icon {
Icon::Bolt => "icons/bolt.svg",
Icon::CaseSensitive => "icons/case_insensitive.svg",
Icon::Check => "icons/check.svg",
+ Icon::Copy => "icons/copy.svg",
Icon::ChevronDown => "icons/chevron_down.svg",
Icon::ChevronLeft => "icons/chevron_left.svg",
Icon::ChevronRight => "icons/chevron_right.svg",
@@ -126,6 +129,7 @@ impl Icon {
Icon::FolderX => "icons/stop_sharing.svg",
Icon::Hash => "icons/hash.svg",
Icon::InlayHint => "icons/inlay_hint.svg",
+ Icon::Link => "icons/link.svg",
Icon::MagicWand => "icons/magic-wand.svg",
Icon::MagnifyingGlass => "icons/magnifying_glass.svg",
Icon::MailOpen => "icons/mail-open.svg",
@@ -1,6 +1,8 @@
+use std::ops::Range;
+
use crate::prelude::*;
use crate::styled_ext::StyledExt;
-use gpui::{relative, Div, IntoElement, StyledText, TextRun, WindowContext};
+use gpui::{relative, Div, HighlightStyle, IntoElement, StyledText, WindowContext};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum LabelSize {
@@ -99,38 +101,32 @@ impl RenderOnce for HighlightedLabel {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let highlight_color = cx.theme().colors().text_accent;
- let mut text_style = cx.text_style().clone();
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
-
- let mut runs: Vec<TextRun> = Vec::new();
-
- for (char_ix, char) in self.label.char_indices() {
- let mut color = self.color.color(cx);
-
- if let Some(highlight_ix) = highlight_indices.peek() {
- if char_ix == *highlight_ix {
- color = highlight_color;
- highlight_indices.next();
+ let mut highlights: Vec<(Range<usize>, HighlightStyle)> = Vec::new();
+
+ while let Some(start_ix) = highlight_indices.next() {
+ let mut end_ix = start_ix;
+
+ loop {
+ end_ix = end_ix + self.label[end_ix..].chars().next().unwrap().len_utf8();
+ if let Some(&next_ix) = highlight_indices.peek() {
+ if next_ix == end_ix {
+ end_ix = next_ix;
+ highlight_indices.next();
+ continue;
+ }
}
+ break;
}
- let last_run = runs.last_mut();
- let start_new_run = if let Some(last_run) = last_run {
- if color == last_run.color {
- last_run.len += char.len_utf8();
- false
- } else {
- true
- }
- } else {
- true
- };
-
- if start_new_run {
- text_style.color = color;
- runs.push(text_style.to_run(char.len_utf8()))
- }
+ highlights.push((
+ start_ix..end_ix,
+ HighlightStyle {
+ color: Some(highlight_color),
+ ..Default::default()
+ },
+ ));
}
div()
@@ -150,7 +146,7 @@ impl RenderOnce for HighlightedLabel {
LabelSize::Default => this.text_ui(),
LabelSize::Small => this.text_ui_sm(),
})
- .child(StyledText::new(self.label).with_runs(runs))
+ .child(StyledText::new(self.label).with_highlights(&cx.text_style(), highlights))
}
}
@@ -0,0 +1,231 @@
+use std::{cell::RefCell, rc::Rc};
+
+use gpui::{
+ overlay, point, px, rems, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
+ Element, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseDownEvent,
+ ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+};
+
+use crate::{Clickable, Selectable};
+
+pub trait PopoverTrigger: IntoElement + Clickable + Selectable + 'static {}
+
+impl<T: IntoElement + Clickable + Selectable + 'static> PopoverTrigger for T {}
+
+pub struct PopoverMenu<M: ManagedView> {
+ id: ElementId,
+ child_builder: Option<
+ Box<
+ dyn FnOnce(
+ Rc<RefCell<Option<View<M>>>>,
+ Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
+ ) -> AnyElement
+ + 'static,
+ >,
+ >,
+ menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
+ anchor: AnchorCorner,
+ attach: Option<AnchorCorner>,
+ offset: Option<Point<Pixels>>,
+}
+
+impl<M: ManagedView> PopoverMenu<M> {
+ pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> View<M> + 'static) -> Self {
+ self.menu_builder = Some(Rc::new(f));
+ self
+ }
+
+ pub fn trigger<T: PopoverTrigger>(mut self, t: T) -> Self {
+ self.child_builder = Some(Box::new(|menu, builder| {
+ let open = menu.borrow().is_some();
+ t.selected(open)
+ .when_some(builder, |el, builder| {
+ el.on_click({
+ move |_, cx| {
+ let new_menu = (builder)(cx);
+ let menu2 = menu.clone();
+ let previous_focus_handle = cx.focused();
+
+ cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
+ if modal.focus_handle(cx).contains_focused(cx) {
+ if previous_focus_handle.is_some() {
+ cx.focus(&previous_focus_handle.as_ref().unwrap())
+ }
+ }
+ *menu2.borrow_mut() = None;
+ cx.notify();
+ })
+ .detach();
+ cx.focus_view(&new_menu);
+ *menu.borrow_mut() = Some(new_menu);
+ }
+ })
+ })
+ .into_any_element()
+ }));
+ self
+ }
+
+ /// anchor defines which corner of the menu to anchor to the attachment point
+ /// (by default the cursor position, but see attach)
+ pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
+ self.anchor = anchor;
+ self
+ }
+
+ /// attach defines which corner of the handle to attach the menu's anchor to
+ pub fn attach(mut self, attach: AnchorCorner) -> Self {
+ self.attach = Some(attach);
+ self
+ }
+
+ /// offset offsets the position of the content by that many pixels.
+ pub fn offset(mut self, offset: Point<Pixels>) -> Self {
+ self.offset = Some(offset);
+ self
+ }
+
+ fn resolved_attach(&self) -> AnchorCorner {
+ self.attach.unwrap_or_else(|| match self.anchor {
+ AnchorCorner::TopLeft => AnchorCorner::BottomLeft,
+ AnchorCorner::TopRight => AnchorCorner::BottomRight,
+ AnchorCorner::BottomLeft => AnchorCorner::TopLeft,
+ AnchorCorner::BottomRight => AnchorCorner::TopRight,
+ })
+ }
+
+ fn resolved_offset(&self, cx: &WindowContext) -> Point<Pixels> {
+ self.offset.unwrap_or_else(|| {
+ // Default offset = 4px padding + 1px border
+ let offset = rems(5. / 16.) * cx.rem_size();
+ match self.anchor {
+ AnchorCorner::TopRight | AnchorCorner::BottomRight => point(offset, px(0.)),
+ AnchorCorner::TopLeft | AnchorCorner::BottomLeft => point(-offset, px(0.)),
+ }
+ })
+ }
+}
+
+pub fn popover_menu<M: ManagedView>(id: impl Into<ElementId>) -> PopoverMenu<M> {
+ PopoverMenu {
+ id: id.into(),
+ child_builder: None,
+ menu_builder: None,
+ anchor: AnchorCorner::TopLeft,
+ attach: None,
+ offset: None,
+ }
+}
+
+pub struct PopoverMenuState<M> {
+ child_layout_id: Option<LayoutId>,
+ child_element: Option<AnyElement>,
+ child_bounds: Option<Bounds<Pixels>>,
+ menu_element: Option<AnyElement>,
+ menu: Rc<RefCell<Option<View<M>>>>,
+}
+
+impl<M: ManagedView> Element for PopoverMenu<M> {
+ type State = PopoverMenuState<M>;
+
+ fn layout(
+ &mut self,
+ element_state: Option<Self::State>,
+ cx: &mut WindowContext,
+ ) -> (gpui::LayoutId, Self::State) {
+ let mut menu_layout_id = None;
+
+ let (menu, child_bounds) = if let Some(element_state) = element_state {
+ (element_state.menu, element_state.child_bounds)
+ } else {
+ (Rc::default(), None)
+ };
+
+ let menu_element = menu.borrow_mut().as_mut().map(|menu| {
+ let mut overlay = overlay().snap_to_window().anchor(self.anchor);
+
+ if let Some(child_bounds) = child_bounds {
+ overlay = overlay.position(
+ self.resolved_attach().corner(child_bounds) + self.resolved_offset(cx),
+ );
+ }
+
+ let mut element = overlay.child(menu.clone()).into_any();
+ menu_layout_id = Some(element.layout(cx));
+ element
+ });
+
+ let mut child_element = self
+ .child_builder
+ .take()
+ .map(|child_builder| (child_builder)(menu.clone(), self.menu_builder.clone()));
+
+ let child_layout_id = child_element
+ .as_mut()
+ .map(|child_element| child_element.layout(cx));
+
+ let layout_id = cx.request_layout(
+ &gpui::Style::default(),
+ menu_layout_id.into_iter().chain(child_layout_id),
+ );
+
+ (
+ layout_id,
+ PopoverMenuState {
+ menu,
+ child_element,
+ child_layout_id,
+ menu_element,
+ child_bounds,
+ },
+ )
+ }
+
+ fn paint(
+ self,
+ _: Bounds<gpui::Pixels>,
+ element_state: &mut Self::State,
+ cx: &mut WindowContext,
+ ) {
+ if let Some(child) = element_state.child_element.take() {
+ child.paint(cx);
+ }
+
+ if let Some(child_layout_id) = element_state.child_layout_id.take() {
+ element_state.child_bounds = Some(cx.layout_bounds(child_layout_id));
+ }
+
+ if let Some(menu) = element_state.menu_element.take() {
+ menu.paint(cx);
+
+ if let Some(child_bounds) = element_state.child_bounds {
+ let interactive_bounds = InteractiveBounds {
+ bounds: child_bounds,
+ stacking_order: cx.stacking_order().clone(),
+ };
+
+ // Mouse-downing outside the menu dismisses it, so we don't
+ // want a click on the toggle to re-open it.
+ cx.on_mouse_event(move |e: &MouseDownEvent, phase, cx| {
+ if phase == DispatchPhase::Bubble
+ && interactive_bounds.visibly_contains(&e.position, cx)
+ {
+ cx.stop_propagation()
+ }
+ })
+ }
+ }
+ }
+}
+
+impl<M: ManagedView> IntoElement for PopoverMenu<M> {
+ type Element = Self;
+
+ fn element_id(&self) -> Option<gpui::ElementId> {
+ Some(self.id.clone())
+ }
+
+ fn into_element(self) -> Self::Element {
+ self
+ }
+}
@@ -0,0 +1,185 @@
+use std::{cell::RefCell, rc::Rc};
+
+use gpui::{
+ overlay, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element, ElementId,
+ IntoElement, LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point,
+ View, VisualContext, WindowContext,
+};
+
+pub struct RightClickMenu<M: ManagedView> {
+ id: ElementId,
+ child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement + 'static>>,
+ menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
+ anchor: Option<AnchorCorner>,
+ attach: Option<AnchorCorner>,
+}
+
+impl<M: ManagedView> RightClickMenu<M> {
+ pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> View<M> + 'static) -> Self {
+ self.menu_builder = Some(Rc::new(f));
+ self
+ }
+
+ pub fn trigger<E: IntoElement + 'static>(mut self, e: E) -> Self {
+ self.child_builder = Some(Box::new(move |_| e.into_any_element()));
+ self
+ }
+
+ /// anchor defines which corner of the menu to anchor to the attachment point
+ /// (by default the cursor position, but see attach)
+ pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
+ self.anchor = Some(anchor);
+ self
+ }
+
+ /// attach defines which corner of the handle to attach the menu's anchor to
+ pub fn attach(mut self, attach: AnchorCorner) -> Self {
+ self.attach = Some(attach);
+ self
+ }
+}
+
+pub fn right_click_menu<M: ManagedView>(id: impl Into<ElementId>) -> RightClickMenu<M> {
+ RightClickMenu {
+ id: id.into(),
+ child_builder: None,
+ menu_builder: None,
+ anchor: None,
+ attach: None,
+ }
+}
+
+pub struct MenuHandleState<M> {
+ menu: Rc<RefCell<Option<View<M>>>>,
+ position: Rc<RefCell<Point<Pixels>>>,
+ child_layout_id: Option<LayoutId>,
+ child_element: Option<AnyElement>,
+ menu_element: Option<AnyElement>,
+}
+
+impl<M: ManagedView> Element for RightClickMenu<M> {
+ type State = MenuHandleState<M>;
+
+ fn layout(
+ &mut self,
+ element_state: Option<Self::State>,
+ cx: &mut WindowContext,
+ ) -> (gpui::LayoutId, Self::State) {
+ let (menu, position) = if let Some(element_state) = element_state {
+ (element_state.menu, element_state.position)
+ } else {
+ (Rc::default(), Rc::default())
+ };
+
+ let mut menu_layout_id = None;
+
+ let menu_element = menu.borrow_mut().as_mut().map(|menu| {
+ let mut overlay = overlay().snap_to_window();
+ if let Some(anchor) = self.anchor {
+ overlay = overlay.anchor(anchor);
+ }
+ overlay = overlay.position(*position.borrow());
+
+ let mut element = overlay.child(menu.clone()).into_any();
+ menu_layout_id = Some(element.layout(cx));
+ element
+ });
+
+ let mut child_element = self
+ .child_builder
+ .take()
+ .map(|child_builder| (child_builder)(menu.borrow().is_some()));
+
+ let child_layout_id = child_element
+ .as_mut()
+ .map(|child_element| child_element.layout(cx));
+
+ let layout_id = cx.request_layout(
+ &gpui::Style::default(),
+ menu_layout_id.into_iter().chain(child_layout_id),
+ );
+
+ (
+ layout_id,
+ MenuHandleState {
+ menu,
+ position,
+ child_element,
+ child_layout_id,
+ menu_element,
+ },
+ )
+ }
+
+ fn paint(
+ self,
+ bounds: Bounds<gpui::Pixels>,
+ element_state: &mut Self::State,
+ cx: &mut WindowContext,
+ ) {
+ if let Some(child) = element_state.child_element.take() {
+ child.paint(cx);
+ }
+
+ if let Some(menu) = element_state.menu_element.take() {
+ menu.paint(cx);
+ return;
+ }
+
+ let Some(builder) = self.menu_builder else {
+ return;
+ };
+ let menu = element_state.menu.clone();
+ let position = element_state.position.clone();
+ let attach = self.attach.clone();
+ let child_layout_id = element_state.child_layout_id.clone();
+
+ cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
+ if phase == DispatchPhase::Bubble
+ && event.button == MouseButton::Right
+ && bounds.contains_point(&event.position)
+ {
+ cx.stop_propagation();
+ cx.prevent_default();
+
+ let new_menu = (builder)(cx);
+ let menu2 = menu.clone();
+ let previous_focus_handle = cx.focused();
+
+ cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
+ if modal.focus_handle(cx).contains_focused(cx) {
+ if previous_focus_handle.is_some() {
+ cx.focus(&previous_focus_handle.as_ref().unwrap())
+ }
+ }
+ *menu2.borrow_mut() = None;
+ cx.notify();
+ })
+ .detach();
+ cx.focus_view(&new_menu);
+ *menu.borrow_mut() = Some(new_menu);
+
+ *position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() {
+ attach
+ .unwrap()
+ .corner(cx.layout_bounds(child_layout_id.unwrap()))
+ } else {
+ cx.mouse_position()
+ };
+ cx.notify();
+ }
+ });
+ }
+}
+
+impl<M: ManagedView> IntoElement for RightClickMenu<M> {
+ type Element = Self;
+
+ fn element_id(&self) -> Option<gpui::ElementId> {
+ Some(self.id.clone())
+ }
+
+ fn into_element(self) -> Self::Element {
+ self
+ }
+}
@@ -1,7 +1,7 @@
use gpui::{Div, Render};
use story::Story;
-use crate::prelude::*;
+use crate::{prelude::*, Icon};
use crate::{Button, ButtonStyle};
pub struct ButtonStory;
@@ -16,8 +16,22 @@ impl Render for ButtonStory {
.child(Button::new("default_filled", "Click me"))
.child(Story::label("Selected"))
.child(Button::new("selected_filled", "Click me").selected(true))
+ .child(Story::label("Selected with `selected_label`"))
+ .child(
+ Button::new("selected_label_filled", "Click me")
+ .selected(true)
+ .selected_label("I have been selected"),
+ )
.child(Story::label("With `label_color`"))
.child(Button::new("filled_with_label_color", "Click me").color(Color::Created))
+ .child(Story::label("With `icon`"))
+ .child(Button::new("filled_with_icon", "Click me").icon(Icon::FileGit))
+ .child(Story::label("Selected with `icon`"))
+ .child(
+ Button::new("filled_and_selected_with_icon", "Click me")
+ .selected(true)
+ .icon(Icon::FileGit),
+ )
.child(Story::label("Default (Subtle)"))
.child(Button::new("default_subtle", "Click me").style(ButtonStyle::Subtle))
.child(Story::label("Default (Transparent)"))
@@ -2,7 +2,7 @@ use gpui::{actions, Action, AnchorCorner, Div, Render, View};
use story::Story;
use crate::prelude::*;
-use crate::{menu_handle, ContextMenu, Label};
+use crate::{right_click_menu, ContextMenu, Label};
actions!(PrintCurrentDate, PrintBestFood);
@@ -45,25 +45,13 @@ impl Render for ContextMenuStory {
.flex_col()
.justify_between()
.child(
- menu_handle("test2")
- .child(|is_open| {
- Label::new(if is_open {
- "TOP LEFT"
- } else {
- "RIGHT CLICK ME"
- })
- })
+ right_click_menu("test2")
+ .trigger(Label::new("TOP LEFT"))
.menu(move |cx| build_menu(cx, "top left")),
)
.child(
- menu_handle("test1")
- .child(|is_open| {
- Label::new(if is_open {
- "BOTTOM LEFT"
- } else {
- "RIGHT CLICK ME"
- })
- })
+ right_click_menu("test1")
+ .trigger(Label::new("BOTTOM LEFT"))
.anchor(AnchorCorner::BottomLeft)
.attach(AnchorCorner::TopLeft)
.menu(move |cx| build_menu(cx, "bottom left")),
@@ -75,26 +63,14 @@ impl Render for ContextMenuStory {
.flex_col()
.justify_between()
.child(
- menu_handle("test3")
- .child(|is_open| {
- Label::new(if is_open {
- "TOP RIGHT"
- } else {
- "RIGHT CLICK ME"
- })
- })
+ right_click_menu("test3")
+ .trigger(Label::new("TOP RIGHT"))
.anchor(AnchorCorner::TopRight)
.menu(move |cx| build_menu(cx, "top right")),
)
.child(
- menu_handle("test4")
- .child(|is_open| {
- Label::new(if is_open {
- "BOTTOM RIGHT"
- } else {
- "RIGHT CLICK ME"
- })
- })
+ right_click_menu("test4")
+ .trigger(Label::new("BOTTOM RIGHT"))
.anchor(AnchorCorner::BottomRight)
.attach(AnchorCorner::TopRight)
.menu(move |cx| build_menu(cx, "bottom right")),
@@ -20,6 +20,14 @@ impl Render for IconButtonStory {
.w_8()
.child(IconButton::new("icon_a", Icon::Hash).selected(true)),
)
+ .child(Story::label("Selected with `selected_icon`"))
+ .child(
+ div().w_8().child(
+ IconButton::new("icon_a", Icon::AudioOn)
+ .selected(true)
+ .selected_icon(Icon::AudioOff),
+ ),
+ )
.child(Story::label("Disabled"))
.child(
div()
@@ -1,4 +1,6 @@
use gpui::{px, Styled, WindowContext};
+use settings::Settings;
+use theme::ThemeSettings;
use crate::prelude::*;
use crate::{ElevationIndex, UITextSize};
@@ -60,6 +62,18 @@ pub trait StyledExt: Styled + Sized {
self.text_size(size)
}
+ /// The font size for buffer text.
+ ///
+ /// Retrieves the default font size, or the user's custom font size if set.
+ ///
+ /// This should only be used for text that is displayed in a buffer,
+ /// or other places that text needs to match the user's buffer font size.
+ fn text_buffer(self, cx: &mut WindowContext) -> Self {
+ let settings = ThemeSettings::get_global(cx);
+
+ self.text_size(settings.buffer_font_size)
+ }
+
/// The [`Surface`](ui2::ElevationIndex::Surface) elevation level, located above the app background, is the standard level for all elements
///
/// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
@@ -7,8 +7,8 @@ use gpui::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
-use ui::prelude::*;
-use ui::{h_stack, menu_handle, ContextMenu, IconButton, Tooltip};
+use ui::{h_stack, ContextMenu, IconButton, Tooltip};
+use ui::{prelude::*, right_click_menu};
pub enum PanelEvent {
ChangePosition,
@@ -702,7 +702,7 @@ impl Render for PanelButtons {
};
Some(
- menu_handle(name)
+ right_click_menu(name)
.menu(move |cx| {
const POSITIONS: [DockPosition; 3] = [
DockPosition::Left,
@@ -726,14 +726,14 @@ impl Render for PanelButtons {
})
.anchor(menu_anchor)
.attach(menu_attach)
- .child(move |_is_open| {
+ .trigger(
IconButton::new(name, icon)
.selected(is_active_button)
.action(action.boxed_clone())
.tooltip(move |cx| {
Tooltip::for_action(tooltip.clone(), &*action, cx)
- })
- }),
+ }),
+ ),
)
});
@@ -135,24 +135,22 @@ impl Workspace {
}
pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext<Self>) {
- todo!()
- // self.dismiss_notification::<simple_message_notification::MessageNotification>(toast.id, cx);
- // self.show_notification(toast.id, cx, |cx| {
- // cx.add_view(|_cx| match toast.on_click.as_ref() {
- // Some((click_msg, on_click)) => {
- // let on_click = on_click.clone();
- // simple_message_notification::MessageNotification::new(toast.msg.clone())
- // .with_click_message(click_msg.clone())
- // .on_click(move |cx| on_click(cx))
- // }
- // None => simple_message_notification::MessageNotification::new(toast.msg.clone()),
- // })
- // })
+ self.dismiss_notification::<simple_message_notification::MessageNotification>(toast.id, cx);
+ self.show_notification(toast.id, cx, |cx| {
+ cx.build_view(|_cx| match toast.on_click.as_ref() {
+ Some((click_msg, on_click)) => {
+ let on_click = on_click.clone();
+ simple_message_notification::MessageNotification::new(toast.msg.clone())
+ .with_click_message(click_msg.clone())
+ .on_click(move |cx| on_click(cx))
+ }
+ None => simple_message_notification::MessageNotification::new(toast.msg.clone()),
+ })
+ })
}
pub fn dismiss_toast(&mut self, id: usize, cx: &mut ViewContext<Self>) {
- todo!()
- // self.dismiss_notification::<simple_message_notification::MessageNotification>(id, cx);
+ self.dismiss_notification::<simple_message_notification::MessageNotification>(id, cx);
}
fn dismiss_notification_internal(
@@ -179,33 +177,10 @@ pub mod simple_message_notification {
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, TextStyle,
ViewContext,
};
- use serde::Deserialize;
- use std::{borrow::Cow, sync::Arc};
+ use std::sync::Arc;
use ui::prelude::*;
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
- #[derive(Clone, Default, Deserialize, PartialEq)]
- pub struct OsOpen(pub Cow<'static, str>);
-
- impl OsOpen {
- pub fn new<I: Into<Cow<'static, str>>>(url: I) -> Self {
- OsOpen(url.into())
- }
- }
-
- // todo!()
- // impl_actions!(message_notifications, [OsOpen]);
- //
- // todo!()
- // pub fn init(cx: &mut AppContext) {
- // cx.add_action(MessageNotification::dismiss);
- // cx.add_action(
- // |_workspace: &mut Workspace, open_action: &OsOpen, cx: &mut ViewContext<Workspace>| {
- // cx.platform().open_url(open_action.0.as_ref());
- // },
- // )
- // }
-
enum NotificationMessage {
Text(SharedString),
Element(fn(TextStyle, &AppContext) -> AnyElement),
@@ -213,7 +188,7 @@ pub mod simple_message_notification {
pub struct MessageNotification {
message: NotificationMessage,
- on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>) + Send + Sync>>,
+ on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>)>>,
click_message: Option<SharedString>,
}
@@ -252,7 +227,7 @@ pub mod simple_message_notification {
pub fn on_click<F>(mut self, on_click: F) -> Self
where
- F: 'static + Send + Sync + Fn(&mut ViewContext<Self>),
+ F: 'static + Fn(&mut ViewContext<Self>),
{
self.on_click = Some(Arc::new(on_click));
self
@@ -2,14 +2,15 @@ use crate::{
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
workspace_settings::{AutosaveSetting, WorkspaceSettings},
- SplitDirection, Workspace,
+ NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
use gpui::{
- actions, prelude::*, Action, AppContext, AsyncWindowContext, Div, EntityId, EventEmitter,
- FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View,
- ViewContext, VisualContext, WeakView, WindowContext,
+ actions, overlay, prelude::*, rems, Action, AnchorCorner, AnyWeakView, AppContext,
+ AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
+ FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View, ViewContext,
+ VisualContext, WeakView, WindowContext,
};
use parking_lot::Mutex;
use project::{Project, ProjectEntryId, ProjectPath};
@@ -25,8 +26,10 @@ use std::{
},
};
-use ui::v_stack;
-use ui::{prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
+use ui::{
+ h_stack, prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Label, Tooltip,
+};
+use ui::{v_stack, ContextMenu};
use util::truncate_and_remove_front;
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
@@ -50,7 +53,7 @@ pub enum SaveIntent {
//todo!("Do we need the default bound on actions? Decide soon")
// #[register_action]
-#[derive(Clone, Deserialize, PartialEq, Debug)]
+#[derive(Action, Clone, Deserialize, PartialEq, Debug)]
pub struct ActivateItem(pub usize);
// #[derive(Clone, PartialEq)]
@@ -143,17 +146,24 @@ impl fmt::Debug for Event {
}
}
+struct FocusedView {
+ view: AnyWeakView,
+ focus_handle: FocusHandle,
+}
+
pub struct Pane {
focus_handle: FocusHandle,
items: Vec<Box<dyn ItemHandle>>,
activation_history: Vec<EntityId>,
zoomed: bool,
active_item_index: usize,
- // last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>,
+ last_focused_view_by_item: HashMap<EntityId, FocusHandle>,
autoscroll: bool,
nav_history: NavHistory,
toolbar: View<Toolbar>,
- // tab_bar_context_menu: TabBarContextMenu,
+ tab_bar_focus_handle: FocusHandle,
+ new_item_menu: Option<View<ContextMenu>>,
+ split_item_menu: Option<View<ContextMenu>>,
// tab_context_menu: ViewHandle<ContextMenu>,
workspace: WeakView<Workspace>,
project: Model<Project>,
@@ -306,7 +316,7 @@ impl Pane {
activation_history: Vec::new(),
zoomed: false,
active_item_index: 0,
- // last_focused_view_by_item: Default::default(),
+ last_focused_view_by_item: Default::default(),
autoscroll: false,
nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState {
mode: NavigationMode::Normal,
@@ -318,6 +328,9 @@ impl Pane {
next_timestamp,
}))),
toolbar: cx.build_view(|_| Toolbar::new()),
+ tab_bar_focus_handle: cx.focus_handle(),
+ new_item_menu: None,
+ split_item_menu: None,
// tab_bar_context_menu: TabBarContextMenu {
// kind: TabBarContextMenuKind::New,
// handle: context_menu,
@@ -392,9 +405,48 @@ impl Pane {
}
pub fn has_focus(&self, cx: &WindowContext) -> bool {
+ // todo!(); // inline this manually
self.focus_handle.contains_focused(cx)
}
+ fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
+ if !self.has_focus(cx) {
+ cx.emit(Event::Focus);
+ cx.notify();
+ }
+
+ self.toolbar.update(cx, |toolbar, cx| {
+ toolbar.focus_changed(true, cx);
+ });
+
+ if let Some(active_item) = self.active_item() {
+ if self.focus_handle.is_focused(cx) {
+ // Pane was focused directly. We need to either focus a view inside the active item,
+ // or focus the active item itself
+ if let Some(weak_last_focused_view) =
+ self.last_focused_view_by_item.get(&active_item.item_id())
+ {
+ weak_last_focused_view.focus(cx);
+ return;
+ }
+
+ active_item.focus_handle(cx).focus(cx);
+ } else if !self.tab_bar_focus_handle.contains_focused(cx) {
+ if let Some(focused) = cx.focused() {
+ self.last_focused_view_by_item
+ .insert(active_item.item_id(), focused);
+ }
+ }
+ }
+ }
+
+ fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
+ self.toolbar.update(cx, |toolbar, cx| {
+ toolbar.focus_changed(false, cx);
+ });
+ cx.notify();
+ }
+
pub fn active_item_index(&self) -> usize {
self.active_item_index
}
@@ -652,21 +704,16 @@ impl Pane {
.position(|i| i.item_id() == item.item_id())
}
- // pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
- // // Potentially warn the user of the new keybinding
- // let workspace_handle = self.workspace().clone();
- // cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) })
- // .detach();
-
- // if self.zoomed {
- // cx.emit(Event::ZoomOut);
- // } else if !self.items.is_empty() {
- // if !self.has_focus {
- // cx.focus_self();
- // }
- // cx.emit(Event::ZoomIn);
- // }
- // }
+ pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
+ if self.zoomed {
+ cx.emit(Event::ZoomOut);
+ } else if !self.items.is_empty() {
+ if !self.focus_handle.contains_focused(cx) {
+ cx.focus_self();
+ }
+ cx.emit(Event::ZoomIn);
+ }
+ }
pub fn activate_item(
&mut self,
@@ -1403,7 +1450,7 @@ impl Pane {
let close_right = ItemSettings::get_global(cx).close_position.right();
let is_active = ix == self.active_item_index;
- div()
+ let tab = div()
.group("")
.id(ix)
.cursor_pointer()
@@ -1477,51 +1524,75 @@ impl Pane {
.children((!close_right).then(|| close_icon()))
.child(label)
.children(close_right.then(|| close_icon())),
- )
+ );
+
+ right_click_menu(ix).trigger(tab).menu(|cx| {
+ ContextMenu::build(cx, |menu, cx| {
+ menu.action(
+ "Close Active Item",
+ CloseActiveItem { save_intent: None }.boxed_clone(),
+ cx,
+ )
+ .action("Close Inactive Items", CloseInactiveItems.boxed_clone(), cx)
+ .action("Close Clean Items", CloseCleanItems.boxed_clone(), cx)
+ .action(
+ "Close Items To The Left",
+ CloseItemsToTheLeft.boxed_clone(),
+ cx,
+ )
+ .action(
+ "Close Items To The Right",
+ CloseItemsToTheRight.boxed_clone(),
+ cx,
+ )
+ .action(
+ "Close All Items",
+ CloseAllItems { save_intent: None }.boxed_clone(),
+ cx,
+ )
+ })
+ })
}
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
div()
- .group("tab_bar")
.id("tab_bar")
+ .group("tab_bar")
+ .track_focus(&self.tab_bar_focus_handle)
.w_full()
+ // 30px @ 16px/rem
+ .h(rems(1.875))
+ .overflow_hidden()
.flex()
+ .flex_none()
.bg(cx.theme().colors().tab_bar_background)
// Left Side
.child(
- div()
- .relative()
- .px_1()
+ h_stack()
+ .px_2()
.flex()
.flex_none()
- .gap_2()
+ .gap_1()
// Nav Buttons
.child(
- div()
- .right_0()
- .flex()
- .items_center()
- .gap_px()
- .child(
- div().border().border_color(gpui::red()).child(
- IconButton::new("navigate_backward", Icon::ArrowLeft)
- .on_click({
- let view = cx.view().clone();
- move |_, cx| view.update(cx, Self::navigate_backward)
- })
- .disabled(!self.can_navigate_backward()),
- ),
- )
- .child(
- div().border().border_color(gpui::red()).child(
- IconButton::new("navigate_forward", Icon::ArrowRight)
- .on_click({
- let view = cx.view().clone();
- move |_, cx| view.update(cx, Self::navigate_backward)
- })
- .disabled(!self.can_navigate_forward()),
- ),
- ),
+ div().border().border_color(gpui::red()).child(
+ IconButton::new("navigate_backward", Icon::ArrowLeft)
+ .on_click({
+ let view = cx.view().clone();
+ move |_, cx| view.update(cx, Self::navigate_backward)
+ })
+ .disabled(!self.can_navigate_backward()),
+ ),
+ )
+ .child(
+ div().border().border_color(gpui::red()).child(
+ IconButton::new("navigate_forward", Icon::ArrowRight)
+ .on_click({
+ let view = cx.view().clone();
+ move |_, cx| view.update(cx, Self::navigate_backward)
+ })
+ .disabled(!self.can_navigate_forward()),
+ ),
),
)
.child(
@@ -1550,20 +1621,87 @@ impl Pane {
.gap_px()
.child(
div()
+ .bg(gpui::blue())
.border()
.border_color(gpui::red())
- .child(IconButton::new("plus", Icon::Plus)),
+ .child(IconButton::new("plus", Icon::Plus).on_click(
+ cx.listener(|this, _, cx| {
+ let menu = ContextMenu::build(cx, |menu, cx| {
+ menu.action("New File", NewFile.boxed_clone(), cx)
+ .action(
+ "New Terminal",
+ NewCenterTerminal.boxed_clone(),
+ cx,
+ )
+ .action(
+ "New Search",
+ NewSearch.boxed_clone(),
+ cx,
+ )
+ });
+ cx.subscribe(
+ &menu,
+ |this, _, event: &DismissEvent, cx| {
+ this.focus(cx);
+ this.new_item_menu = None;
+ },
+ )
+ .detach();
+ this.new_item_menu = Some(menu);
+ }),
+ ))
+ .when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
+ el.child(Self::render_menu_overlay(new_item_menu))
+ }),
)
.child(
div()
.border()
.border_color(gpui::red())
- .child(IconButton::new("split", Icon::Split)),
+ .child(IconButton::new("split", Icon::Split).on_click(
+ cx.listener(|this, _, cx| {
+ let menu = ContextMenu::build(cx, |menu, cx| {
+ menu.action(
+ "Split Right",
+ SplitRight.boxed_clone(),
+ cx,
+ )
+ .action("Split Left", SplitLeft.boxed_clone(), cx)
+ .action("Split Up", SplitUp.boxed_clone(), cx)
+ .action("Split Down", SplitDown.boxed_clone(), cx)
+ });
+ cx.subscribe(
+ &menu,
+ |this, _, event: &DismissEvent, cx| {
+ this.focus(cx);
+ this.split_item_menu = None;
+ },
+ )
+ .detach();
+ this.split_item_menu = Some(menu);
+ }),
+ ))
+ .when_some(
+ self.split_item_menu.as_ref(),
+ |el, split_item_menu| {
+ el.child(Self::render_menu_overlay(split_item_menu))
+ },
+ ),
),
),
)
}
+ fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
+ div()
+ .absolute()
+ .z_index(1)
+ .bottom_0()
+ .right_0()
+ .size_0()
+ .child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
+ }
+
// fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
// let theme = theme::current(cx).clone();
@@ -1962,9 +2100,23 @@ impl Render for Pane {
type Element = Focusable<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let this = cx.view().downgrade();
+
v_stack()
.key_context("Pane")
.track_focus(&self.focus_handle)
+ .on_focus_in({
+ let this = this.clone();
+ move |event, cx| {
+ this.update(cx, |this, cx| this.focus_in(cx)).ok();
+ }
+ })
+ .on_focus_out({
+ let this = this.clone();
+ move |event, cx| {
+ this.update(cx, |this, cx| this.focus_out(cx)).ok();
+ }
+ })
.on_action(cx.listener(|pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx)))
.on_action(cx.listener(|pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)))
.on_action(
@@ -1973,25 +2125,53 @@ impl Render for Pane {
.on_action(cx.listener(|pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)))
.on_action(cx.listener(|pane, _: &GoBack, cx| pane.navigate_backward(cx)))
.on_action(cx.listener(|pane, _: &GoForward, cx| pane.navigate_forward(cx)))
- // cx.add_action(Pane::toggle_zoom);
- // cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
- // pane.activate_item(action.0, true, true, cx);
- // });
- // cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
- // pane.activate_item(pane.items.len() - 1, true, true, cx);
- // });
- // cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
- // pane.activate_prev_item(true, cx);
- // });
- // cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
- // pane.activate_next_item(true, cx);
- // });
- // cx.add_async_action(Pane::close_active_item);
- // cx.add_async_action(Pane::close_inactive_items);
- // cx.add_async_action(Pane::close_clean_items);
- // cx.add_async_action(Pane::close_items_to_the_left);
- // cx.add_async_action(Pane::close_items_to_the_right);
- // cx.add_async_action(Pane::close_all_items);
+ .on_action(cx.listener(Pane::toggle_zoom))
+ .on_action(cx.listener(|pane: &mut Pane, action: &ActivateItem, cx| {
+ pane.activate_item(action.0, true, true, cx);
+ }))
+ .on_action(cx.listener(|pane: &mut Pane, _: &ActivateLastItem, cx| {
+ pane.activate_item(pane.items.len() - 1, true, true, cx);
+ }))
+ .on_action(cx.listener(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
+ pane.activate_prev_item(true, cx);
+ }))
+ .on_action(cx.listener(|pane: &mut Pane, _: &ActivateNextItem, cx| {
+ pane.activate_next_item(true, cx);
+ }))
+ .on_action(
+ cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
+ pane.close_active_item(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ }),
+ )
+ .on_action(
+ cx.listener(|pane: &mut Self, action: &CloseInactiveItems, cx| {
+ pane.close_inactive_items(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ }),
+ )
+ .on_action(
+ cx.listener(|pane: &mut Self, action: &CloseCleanItems, cx| {
+ pane.close_clean_items(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ }),
+ )
+ .on_action(
+ cx.listener(|pane: &mut Self, action: &CloseItemsToTheLeft, cx| {
+ pane.close_items_to_the_left(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ }),
+ )
+ .on_action(
+ cx.listener(|pane: &mut Self, action: &CloseItemsToTheRight, cx| {
+ pane.close_items_to_the_right(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ }),
+ )
+ .on_action(cx.listener(|pane: &mut Self, action: &CloseAllItems, cx| {
+ pane.close_all_items(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ }))
.size_full()
.on_action(
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
@@ -2004,8 +2184,11 @@ impl Render for Pane {
.child(if let Some(item) = self.active_item() {
div().flex().flex_1().child(item.to_any())
} else {
- // todo!()
- div().child("Empty Pane")
+ h_stack()
+ .items_center()
+ .size_full()
+ .justify_center()
+ .child(Label::new("Open a file or project to get started.").color(Color::Muted))
})
// enum MouseNavigationHandler {}
@@ -6,7 +6,7 @@ use gpui::{
WindowContext,
};
use ui::prelude::*;
-use ui::{h_stack, Button, Icon, IconButton};
+use ui::{h_stack, Icon, IconButton};
use util::ResultExt;
pub trait StatusItemView: Render {
@@ -53,39 +53,11 @@ impl Render for StatusBar {
.gap_4()
.child(
h_stack().gap_1().child(
- // TODO: Language picker
+ // Feedback Tool
div()
.border()
.border_color(gpui::red())
- .child(Button::new("status_buffer_language", "Rust")),
- ),
- )
- .child(
- h_stack()
- .gap_1()
- .child(
- // Github tool
- div()
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("status-copilot", Icon::Copilot)),
- )
- .child(
- // Feedback Tool
- div()
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("status-feedback", Icon::Envelope)),
- ),
- )
- .child(
- // Bottom Dock
- h_stack().gap_1().child(
- // Terminal
- div()
- .border()
- .border_color(gpui::red())
- .child(IconButton::new("status-terminal", Icon::Terminal)),
+ .child(IconButton::new("status-feedback", Icon::Envelope)),
),
)
.child(
@@ -4,7 +4,7 @@ use gpui::{
ViewContext, WindowContext,
};
use ui::prelude::*;
-use ui::{h_stack, v_stack, ButtonLike, Color, Icon, IconButton, Label};
+use ui::{h_stack, v_stack, Icon, IconButton};
pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
@@ -87,17 +87,8 @@ impl Render for Toolbar {
.child(
h_stack()
.justify_between()
- .child(
- // Toolbar left side
- h_stack().border().border_color(gpui::red()).p_1().child(
- ButtonLike::new("breadcrumb")
- .child(Label::new("crates/workspace2/src/toolbar.rs"))
- .child(Label::new("›").color(Color::Muted))
- .child(Label::new("impl Render for Toolbar"))
- .child(Label::new("›").color(Color::Muted))
- .child(Label::new("fn render")),
- ),
- )
+ // Toolbar left side
+ .children(self.items.iter().map(|(child, _)| child.to_any()))
// Toolbar right side
.child(
h_stack()
@@ -116,7 +107,6 @@ impl Render for Toolbar {
),
),
)
- .children(self.items.iter().map(|(child, _)| child.to_any()))
}
}
@@ -3585,87 +3585,6 @@ fn open_items(
})
}
-// todo!()
-// fn notify_of_new_dock(workspace: &WeakView<Workspace>, cx: &mut AsyncAppContext) {
-// const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system";
-// const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key";
-// const MESSAGE_ID: usize = 2;
-
-// if workspace
-// .read_with(cx, |workspace, cx| {
-// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
-// })
-// .unwrap_or(false)
-// {
-// return;
-// }
-
-// if db::kvp::KEY_VALUE_STORE
-// .read_kvp(NEW_DOCK_HINT_KEY)
-// .ok()
-// .flatten()
-// .is_some()
-// {
-// if !workspace
-// .read_with(cx, |workspace, cx| {
-// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
-// })
-// .unwrap_or(false)
-// {
-// cx.update(|cx| {
-// cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
-// let entry = tracker
-// .entry(TypeId::of::<MessageNotification>())
-// .or_default();
-// if !entry.contains(&MESSAGE_ID) {
-// entry.push(MESSAGE_ID);
-// }
-// });
-// });
-// }
-
-// return;
-// }
-
-// cx.spawn(|_| async move {
-// db::kvp::KEY_VALUE_STORE
-// .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string())
-// .await
-// .ok();
-// })
-// .detach();
-
-// workspace
-// .update(cx, |workspace, cx| {
-// workspace.show_notification_once(2, cx, |cx| {
-// cx.build_view(|_| {
-// MessageNotification::new_element(|text, _| {
-// Text::new(
-// "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.",
-// text,
-// )
-// .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| {
-// let code_span_background_color = settings::get::<ThemeSettings>(cx)
-// .theme
-// .editor
-// .document_highlight_read_background;
-
-// cx.scene().push_quad(gpui::Quad {
-// bounds,
-// background: Some(code_span_background_color),
-// border: Default::default(),
-// corner_radii: (2.0).into(),
-// })
-// })
-// .into_any()
-// })
-// .with_click_message("Read more about the new panel system")
-// .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST))
-// })
-// })
-// })
-// .ok();
-
fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
@@ -3719,6 +3638,8 @@ impl Render for Workspace {
.items_start()
.text_color(cx.theme().colors().text)
.bg(cx.theme().colors().background)
+ .border()
+ .border_color(cx.theme().colors().border)
.children(self.titlebar_item.clone())
.child(
div()
@@ -172,7 +172,7 @@ osx_info_plist_exts = ["resources/info/*"]
osx_url_schemes = ["zed-dev"]
[package.metadata.bundle-nightly]
-icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"]
+icon = ["resources/app-icon-nightly@2x.png", "resources/app-icon-nightly.png"]
identifier = "dev.zed.Zed-Nightly"
name = "Zed Nightly"
osx_minimum_system_version = "10.15.7"
@@ -39,7 +39,7 @@ pub enum IsOnlyInstance {
}
pub fn ensure_only_instance() -> IsOnlyInstance {
- if *db::ZED_STATELESS {
+ if *db::ZED_STATELESS || *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
return IsOnlyInstance::Yes;
}
@@ -19,7 +19,7 @@ ai = { package = "ai2", path = "../ai2"}
audio = { package = "audio2", path = "../audio2" }
activity_indicator = { package = "activity_indicator2", path = "../activity_indicator2"}
auto_update = { package = "auto_update2", path = "../auto_update2" }
-# breadcrumbs = { path = "../breadcrumbs" }
+breadcrumbs = { package = "breadcrumbs2", path = "../breadcrumbs2" }
call = { package = "call2", path = "../call2" }
channel = { package = "channel2", path = "../channel2" }
cli = { path = "../cli" }
@@ -30,7 +30,7 @@ command_palette = { package="command_palette2", path = "../command_palette2" }
client = { package = "client2", path = "../client2" }
# clock = { path = "../clock" }
copilot = { package = "copilot2", path = "../copilot2" }
-# copilot_button = { path = "../copilot_button" }
+copilot_button = { package = "copilot_button2", path = "../copilot_button2" }
diagnostics = { package = "diagnostics2", path = "../diagnostics2" }
db = { package = "db2", path = "../db2" }
editor = { package="editor2", path = "../editor2" }
@@ -44,7 +44,7 @@ gpui = { package = "gpui2", path = "../gpui2" }
install_cli = { package = "install_cli2", path = "../install_cli2" }
journal = { package = "journal2", path = "../journal2" }
language = { package = "language2", path = "../language2" }
-# language_selector = { path = "../language_selector" }
+language_selector = { package = "language_selector2", path = "../language_selector2" }
lsp = { package = "lsp2", path = "../lsp2" }
menu = { package = "menu2", path = "../menu2" }
# language_tools = { path = "../language_tools" }
@@ -167,7 +167,7 @@ osx_info_plist_exts = ["resources/info/*"]
osx_url_schemes = ["zed-dev"]
[package.metadata.bundle-nightly]
-icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"]
+icon = ["resources/app-icon-nightly@2x.png", "resources/app-icon-nightly.png"]
identifier = "dev.zed.Zed-Dev"
name = "Zed Nightly"
osx_minimum_system_version = "10.15.7"
@@ -216,7 +216,7 @@ fn main() {
terminal_view::init(cx);
// journal2::init(app_state.clone(), cx);
- // language_selector::init(cx);
+ language_selector::init(cx);
theme_selector::init(cx);
// activity_indicator::init(cx);
// language_tools::init(cx);
@@ -39,7 +39,7 @@ pub enum IsOnlyInstance {
}
pub fn ensure_only_instance() -> IsOnlyInstance {
- if *db::ZED_STATELESS {
+ if *db::ZED_STATELESS || *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
return IsOnlyInstance::Yes;
}
@@ -7,6 +7,7 @@ mod only_instance;
mod open_listener;
pub use assets::*;
+use breadcrumbs::Breadcrumbs;
use collections::VecDeque;
use editor::{Editor, MultiBuffer};
use gpui::{
@@ -95,11 +96,11 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
if let workspace::Event::PaneAdded(pane) = event {
pane.update(cx, |pane, cx| {
pane.toolbar().update(cx, |toolbar, cx| {
- // todo!()
- // let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(workspace));
- // toolbar.add_item(breadcrumbs, cx);
+ let breadcrumbs = cx.build_view(|_| Breadcrumbs::new(workspace));
+ toolbar.add_item(breadcrumbs, cx);
let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
toolbar.add_item(buffer_search_bar.clone(), cx);
+ // todo!()
// let quick_action_bar = cx.add_view(|_| {
// QuickActionBar::new(buffer_search_bar, workspace)
// });
@@ -135,14 +136,14 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
// cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx));
// workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx);
- // let copilot =
- // cx.add_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx));
+ let copilot =
+ cx.build_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx));
let diagnostic_summary =
cx.build_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));
let activity_indicator =
activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx);
- // let active_buffer_language =
- // cx.add_view(|_| language_selector::ActiveBufferLanguage::new(workspace));
+ let active_buffer_language =
+ cx.build_view(|_| language_selector::ActiveBufferLanguage::new(workspace));
// let vim_mode_indicator = cx.add_view(|cx| vim::ModeIndicator::new(cx));
// let feedback_button = cx.add_view(|_| {
// feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)
@@ -153,8 +154,8 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
status_bar.add_left_item(activity_indicator, cx);
// status_bar.add_right_item(feedback_button, cx);
- // status_bar.add_right_item(copilot, cx);
- // status_bar.add_right_item(active_buffer_language, cx);
+ status_bar.add_right_item(copilot, cx);
+ status_bar.add_right_item(active_buffer_language, cx);
// status_bar.add_right_item(vim_mode_indicator, cx);
status_bar.add_right_item(cursor_position, cx);
});
@@ -1,5618 +0,0 @@
-#![feature(prelude_import)]
-#![allow(dead_code, unused_variables)]
-#[prelude_import]
-use std::prelude::rust_2021::*;
-#[macro_use]
-extern crate std;
-use color::black;
-use components::button;
-use element::Element;
-use frame::frame;
-use gpui::{
- geometry::{rect::RectF, vector::vec2f},
- platform::WindowOptions,aa
-};
-use log::LevelFilter;a
-use simplelog::SimpleLogger;
-use themes::{rose_pine, ThemeColors};
-use view::view;a
-mod adapter {
- use crate::element::AnyElement;
- use crate::element::{LayoutContext, PaintContext};
- use gpui::{geometry::rect::RectF, LayoutEngine};aaaa
- use util::ResultExt;
- pub struct Adapter<V>(pub(crate) AnyElement<V>);
- impl<V: 'static> gpui::Element<V> for Adapter<V> {aa
- type LayoutState = Option<LayaoutEngine>;
- type PaintState = ();
- fn layout(
- &mut self,
- constraint: gpui::SizeConstraint,
- view: &mut V,
- cx: &mut LayoutContext<V>,aa
- ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
- cx.push_layout_engine(LayoutEngine::new());
- let node = self.0.layout(view, cx).log_err();a
- if let Some(node) = node {
- let layout_engine = cx.layout_engine().unwrap();
- layout_engine.compute_layout(node, constraint.max).log_err();
- }
- let layout_engine = cx.pop_layout_engine();
- if true {a
- if !layout_engine.is_some() {
- ::core::panicking::panic("assertion failed: layout_engine.is_some()")
- }
- }
- (constraint.max, layout_engine)a
- }
- fn paint(a
- &mut self,
- scene: &mut gpui::SceneBuilder,
- bounds: RectF,
- visible_bounds: RectF,
- layout_engine: &mut Option<LayoutEngine>,
- view: &mut V,
- legacy_cx: &mut gpui::PaintContext<V>,aaa
- ) -> Self::PaintState {
- legacy_cx.push_layout_engine(layout_engine.take().unwrap());
- let mut cx = PaintContext::new(legacy_cx, scene);
- self.0.paint(view, &mut cx).log_err();
- *layout_engine = legacy_cx.pop_layout_engine();
- if true {
- if !layout_engine.is_some() {
- ::core::panicking::panic("assertion failed: layout_engine.is_some()")
- }
- }
- }
- fn rect_for_text_range(
- &self,
- range_utf16: std::ops::Range<usize>,
- bounds: RectF,
- visible_bounds: RectF,
- layout: &Self::LayoutState,
- paint: &Self::PaintState,
- view: &V,
- cx: &gpui::ViewContext<V>,
- ) -> Option<RectF> {
- ::core::panicking::panic("not yet implemented")
- }
- fn debug(
- &self,
- bounds: RectF,
- layout: &Self::LayoutState,
- paint: &Self::PaintState,
- view: &V,
- cx: &gpui::ViewContext<V>,
- ) -> gpui::serde_json::Value {
- ::core::panicking::panic("not yet implemented")
- }
- }
-}
-mod color {
- #![allow(dead_code)]
- use smallvec::SmallVec;
- use std::{num::ParseIntError, ops::Range};
- pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
- let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
- let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
- let b = (hex & 0xFF) as f32 / 255.0;
- Rgba { r, g, b, a: 1.0 }.into()
- }
- pub struct Rgba {
- pub r: f32,
- pub g: f32,
- pub b: f32,
- pub a: f32,
- }
- #[automatically_derived]
- impl ::core::clone::Clone for Rgba {
- #[inline]
- fn clone(&self) -> Rgba {
- let _: ::core::clone::AssertParamIsClone<f32>;
- *self
- }
- }
- #[automatically_derived]
- impl ::core::marker::Copy for Rgba {}
- #[automatically_derived]
- impl ::core::default::Default for Rgba {
- #[inline]
- fn default() -> Rgba {
- Rgba {
- r: ::core::default::Default::default(),
- g: ::core::default::Default::default(),
- b: ::core::default::Default::default(),
- a: ::core::default::Default::default(),
- }
- }
- }
- #[automatically_derived]
- impl ::core::fmt::Debug for Rgba {
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- ::core::fmt::Formatter::debug_struct_field4_finish(
- f, "Rgba", "r", &self.r, "g", &self.g, "b", &self.b, "a", &&self.a,
- )
- }
- }
- pub trait Lerp {
- fn lerp(&self, level: f32) -> Hsla;
- }
- impl Lerp for Range<Hsla> {
- fn lerp(&self, level: f32) -> Hsla {
- let level = level.clamp(0., 1.);
- Hsla {
- h: self.start.h + (level * (self.end.h - self.start.h)),
- s: self.start.s + (level * (self.end.s - self.start.s)),
- l: self.start.l + (level * (self.end.l - self.start.l)),
- a: self.start.a + (level * (self.end.a - self.start.a)),
- }
- }
- }
- impl From<gpui::color::Color> for Rgba {
- fn from(value: gpui::color::Color) -> Self {
- Self {
- r: value.0.r as f32 / 255.0,
- g: value.0.g as f32 / 255.0,
- b: value.0.b as f32 / 255.0,
- a: value.0.a as f32 / 255.0,
- }
- }
- }
- impl From<Hsla> for Rgba {
- fn from(color: Hsla) -> Self {
- let h = color.h;
- let s = color.s;
- let l = color.l;
- let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
- let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
- let m = l - c / 2.0;
- let cm = c + m;
- let xm = x + m;
- let (r, g, b) = match (h * 6.0).floor() as i32 {
- 0 | 6 => (cm, xm, m),
- 1 => (xm, cm, m),
- 2 => (m, cm, xm),
- 3 => (m, xm, cm),
- 4 => (xm, m, cm),
- _ => (cm, m, xm),
- };
- Rgba {
- r,
- g,
- b,
- a: color.a,
- }
- }
- }
- impl TryFrom<&'_ str> for Rgba {
- type Error = ParseIntError;
- fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
- let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
- let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
- let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
- let a = if value.len() > 7 {
- u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
- } else {
- 1.0
- };
- Ok(Rgba { r, g, b, a })
- }
- }
- impl Into<gpui::color::Color> for Rgba {
- fn into(self) -> gpui::color::Color {
- gpui::color::rgba(self.r, self.g, self.b, self.a)
- }
- }
- pub struct Hsla {
- pub h: f32,
- pub s: f32,
- pub l: f32,
- pub a: f32,
- }
- #[automatically_derived]
- impl ::core::default::Default for Hsla {
- #[inline]
- fn default() -> Hsla {
- Hsla {
- h: ::core::default::Default::default(),
- s: ::core::default::Default::default(),
- l: ::core::default::Default::default(),
- a: ::core::default::Default::default(),
- }
- }
- }
- #[automatically_derived]
- impl ::core::marker::Copy for Hsla {}
- #[automatically_derived]
- impl ::core::clone::Clone for Hsla {
- #[inline]
- fn clone(&self) -> Hsla {
- let _: ::core::clone::AssertParamIsClone<f32>;
- *self
- }
- }
- #[automatically_derived]
- impl ::core::fmt::Debug for Hsla {
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- ::core::fmt::Formatter::debug_struct_field4_finish(
- f, "Hsla", "h", &self.h, "s", &self.s, "l", &self.l, "a", &&self.a,
- )
- }
- }
- #[automatically_derived]
- impl ::core::marker::StructuralPartialEq for Hsla {}
- #[automatically_derived]
- impl ::core::cmp::PartialEq for Hsla {
- #[inline]
- fn eq(&self, other: &Hsla) -> bool {
- self.h == other.h && self.s == other.s && self.l == other.l && self.a == other.a
- }
- }
- pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
- Hsla {
- h: h.clamp(0., 1.),
- s: s.clamp(0., 1.),
- l: l.clamp(0., 1.),
- a: a.clamp(0., 1.),
- }
- }
- pub fn black() -> Hsla {
- Hsla {
- h: 0.,
- s: 0.,
- l: 0.,
- a: 1.,
- }
- }
- impl From<Rgba> for Hsla {
- fn from(color: Rgba) -> Self {
- let r = color.r;
- let g = color.g;
- let b = color.b;
- let max = r.max(g.max(b));
- let min = r.min(g.min(b));
- let delta = max - min;
- let l = (max + min) / 2.0;
- let s = if l == 0.0 || l == 1.0 {
- 0.0
- } else if l < 0.5 {
- delta / (2.0 * l)
- } else {
- delta / (2.0 - 2.0 * l)
- };
- let h = if delta == 0.0 {
- 0.0
- } else if max == r {
- ((g - b) / delta).rem_euclid(6.0) / 6.0
- } else if max == g {
- ((b - r) / delta + 2.0) / 6.0
- } else {
- ((r - g) / delta + 4.0) / 6.0
- };
- Hsla {
- h,
- s,
- l,
- a: color.a,
- }
- }
- }
- impl Hsla {
- /// Scales the saturation and lightness by the given values, clamping at 1.0.
- pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
- self.s = (self.s * s).clamp(0., 1.);
- self.l = (self.l * l).clamp(0., 1.);
- self
- }
- /// Increases the saturation of the color by a certain amount, with a max
- /// value of 1.0.
- pub fn saturate(mut self, amount: f32) -> Self {
- self.s += amount;
- self.s = self.s.clamp(0.0, 1.0);
- self
- }
- /// Decreases the saturation of the color by a certain amount, with a min
- /// value of 0.0.
- pub fn desaturate(mut self, amount: f32) -> Self {
- self.s -= amount;
- self.s = self.s.max(0.0);
- if self.s < 0.0 {
- self.s = 0.0;
- }
- self
- }
- /// Brightens the color by increasing the lightness by a certain amount,
- /// with a max value of 1.0.
- pub fn brighten(mut self, amount: f32) -> Self {
- self.l += amount;
- self.l = self.l.clamp(0.0, 1.0);
- self
- }
- /// Darkens the color by decreasing the lightness by a certain amount,
- /// with a max value of 0.0.
- pub fn darken(mut self, amount: f32) -> Self {
- self.l -= amount;
- self.l = self.l.clamp(0.0, 1.0);
- self
- }
- }
- impl From<gpui::color::Color> for Hsla {
- fn from(value: gpui::color::Color) -> Self {
- Rgba::from(value).into()
- }
- }
- impl Into<gpui::color::Color> for Hsla {
- fn into(self) -> gpui::color::Color {
- Rgba::from(self).into()
- }
- }
- pub struct ColorScale {
- colors: SmallVec<[Hsla; 2]>,
- positions: SmallVec<[f32; 2]>,
- }
- pub fn scale<I, C>(colors: I) -> ColorScale
- where
- I: IntoIterator<Item = C>,
- C: Into<Hsla>,
- {
- let mut scale = ColorScale {
- colors: colors.into_iter().map(Into::into).collect(),
- positions: SmallVec::new(),
- };
- let num_colors: f32 = scale.colors.len() as f32 - 1.0;
- scale.positions = (0..scale.colors.len())
- .map(|i| i as f32 / num_colors)
- .collect();
- scale
- }
- impl ColorScale {
- fn at(&self, t: f32) -> Hsla {
- if true {
- if !(0.0 <= t && t <= 1.0) {
- {
- ::core::panicking::panic_fmt(format_args!(
- "t value {0} is out of range. Expected value in range 0.0 to 1.0",
- t,
- ));
- }
- }
- }
- let position = match self
- .positions
- .binary_search_by(|a| a.partial_cmp(&t).unwrap())
- {
- Ok(index) | Err(index) => index,
- };
- let lower_bound = position.saturating_sub(1);
- let upper_bound = position.min(self.colors.len() - 1);
- let lower_color = &self.colors[lower_bound];
- let upper_color = &self.colors[upper_bound];
- match upper_bound.checked_sub(lower_bound) {
- Some(0) | None => *lower_color,
- Some(_) => {
- let interval_t = (t - self.positions[lower_bound])
- / (self.positions[upper_bound] - self.positions[lower_bound]);
- let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
- let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
- let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
- let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
- Hsla { h, s, l, a }
- }
- }
- }
- }
-}
-mod components {
- use crate::{
- element::{Element, ElementMetadata},
- frame,
- text::ArcCow,
- themes::rose_pine,
- };
- use gpui::{platform::MouseButton, ViewContext};
- use gpui_macros::Element;
- use std::{marker::PhantomData, rc::Rc};
- struct ButtonHandlers<V, D> {
- click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
- }
- impl<V, D> Default for ButtonHandlers<V, D> {
- fn default() -> Self {
- Self { click: None }
- }
- }
- #[element_crate = "crate"]
- pub struct Button<V: 'static, D: 'static> {
- metadata: ElementMetadata<V>,
- handlers: ButtonHandlers<V, D>,
- label: Option<ArcCow<'static, str>>,
- icon: Option<ArcCow<'static, str>>,
- data: Rc<D>,
- view_type: PhantomData<V>,
- }
- impl<V: 'static, D: 'static> crate::element::Element<V> for Button<V, D> {
- type Layout = crate::element::AnyElement<V>;
- fn declared_style(&mut self) -> &mut crate::style::OptionalStyle {
- &mut self.metadata.style
- }
- fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
- &mut self.metadata.handlers
- }
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut crate::element::LayoutContext<V>,
- ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
- let mut element = self.render(view, cx).into_any();
- let node_id = element.layout(view, cx)?;
- Ok((node_id, element))
- }
- fn paint<'a>(
- &mut self,
- layout: crate::element::Layout<'a, Self::Layout>,
- view: &mut V,
- cx: &mut crate::element::PaintContext<V>,
- ) -> anyhow::Result<()> {
- layout.from_element.paint(view, cx)?;
- Ok(())
- }
- }
- impl<V: 'static, D: 'static> crate::element::IntoElement<V> for Button<V, D> {
- type Element = Self;
- fn into_element(self) -> Self {
- self
- }
- }
- impl<V: 'static> Button<V, ()> {
- fn new() -> Self {
- Self {
- metadata: Default::default(),
- handlers: ButtonHandlers::default(),
- label: None,
- icon: None,
- data: Rc::new(()),
- view_type: PhantomData,
- }
- }
- pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
- Button {
- metadata: Default::default(),
- handlers: ButtonHandlers::default(),
- label: self.label,
- icon: self.icon,
- data: Rc::new(data),
- view_type: PhantomData,
- }
- }
- }
- impl<V: 'static, D: 'static> Button<V, D> {
- pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
- self.label = Some(label.into());
- self
- }
- pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
- self.icon = Some(icon.into());
- self
- }
- pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
- let data = self.data.clone();
- Element::click(self, MouseButton::Left, move |view, _, cx| {
- handler(view, data.as_ref(), cx);
- })
- }
- }
- pub fn button<V>() -> Button<V, ()> {
- Button::new()
- }
- impl<V: 'static, D: 'static> Button<V, D> {
- fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- let button = frame()
- .fill(rose_pine::dawn().error(0.5))
- .h_4()
- .children(self.label.clone());
- if let Some(handler) = self.handlers.click.clone() {
- let data = self.data.clone();
- button.mouse_down(MouseButton::Left, move |view, event, cx| {
- handler(view, data.as_ref(), cx)
- })
- } else {
- button
- }
- }
- }
-}
-mod element {
- pub use crate::paint_context::PaintContext;
- use crate::{
- adapter::Adapter,
- color::Hsla,
- hoverable::Hoverable,
- style::{Display, Fill, OptionalStyle, Overflow, Position},
- };
- use anyhow::Result;
- pub use gpui::LayoutContext;
- use gpui::{
- geometry::{DefinedLength, Length, OptionalPoint},
- platform::{MouseButton, MouseButtonEvent},
- EngineLayout, EventContext, RenderContext, ViewContext,
- };
- use gpui_macros::tailwind_lengths;
- use std::{
- any::{Any, TypeId},
- cell::Cell,
- rc::Rc,
- };
- pub use taffy::tree::NodeId;
- pub struct Layout<'a, E: ?Sized> {
- pub from_engine: EngineLayout,
- pub from_element: &'a mut E,
- }
- pub struct ElementMetadata<V> {
- pub style: OptionalStyle,
- pub handlers: Vec<EventHandler<V>>,
- }
- pub struct EventHandler<V> {
- handler: Rc<dyn Fn(&mut V, &dyn Any, &mut EventContext<V>)>,
- event_type: TypeId,
- outside_bounds: bool,
- }
- impl<V> Clone for EventHandler<V> {
- fn clone(&self) -> Self {
- Self {
- handler: self.handler.clone(),
- event_type: self.event_type,
- outside_bounds: self.outside_bounds,
- }
- }
- }
- impl<V> Default for ElementMetadata<V> {
- fn default() -> Self {
- Self {
- style: OptionalStyle::default(),
- handlers: Vec::new(),
- }
- }
- }
- pub trait Element<V: 'static>: 'static {
- type Layout: 'static;
- fn declared_style(&mut self) -> &mut OptionalStyle;
- fn computed_style(&mut self) -> &OptionalStyle {
- self.declared_style()
- }
- fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> Result<(NodeId, Self::Layout)>;
- fn paint<'a>(
- &mut self,
- layout: Layout<Self::Layout>,
- view: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Result<()>;
- /// Convert to a dynamically-typed element suitable for layout and paint.
- fn into_any(self) -> AnyElement<V>
- where
- Self: 'static + Sized,
- {
- AnyElement {
- element: Box::new(self) as Box<dyn ElementObject<V>>,
- layout: None,
- }
- }
- fn adapt(self) -> Adapter<V>
- where
- Self: Sized,
- Self: Element<V>,
- {
- Adapter(self.into_any())
- }
- fn click(
- self,
- button: MouseButton,
- handler: impl Fn(&mut V, &MouseButtonEvent, &mut ViewContext<V>) + 'static,
- ) -> Self
- where
- Self: Sized,
- {
- let pressed: Rc<Cell<bool>> = Default::default();
- self.mouse_down(button, {
- let pressed = pressed.clone();
- move |_, _, _| {
- pressed.set(true);
- }
- })
- .mouse_up_outside(button, {
- let pressed = pressed.clone();
- move |_, _, _| {
- pressed.set(false);
- }
- })
- .mouse_up(button, move |view, event, event_cx| {
- if pressed.get() {
- pressed.set(false);
- handler(view, event, event_cx);
- }
- })
- }
- fn mouse_down(
- mut self,
- button: MouseButton,
- handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
- ) -> Self
- where
- Self: Sized,
- {
- self.handlers_mut().push(EventHandler {
- handler: Rc::new(move |view, event, event_cx| {
- let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
- if event.button == button && event.is_down {
- handler(view, event, event_cx);
- }
- }),
- event_type: TypeId::of::<MouseButtonEvent>(),
- outside_bounds: false,
- });
- self
- }
- fn mouse_down_outside(
- mut self,
- button: MouseButton,
- handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
- ) -> Self
- where
- Self: Sized,
- {
- self.handlers_mut().push(EventHandler {
- handler: Rc::new(move |view, event, event_cx| {
- let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
- if event.button == button && event.is_down {
- handler(view, event, event_cx);
- }
- }),
- event_type: TypeId::of::<MouseButtonEvent>(),
- outside_bounds: true,
- });
- self
- }
- fn mouse_up(
- mut self,
- button: MouseButton,
- handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
- ) -> Self
- where
- Self: Sized,
- {
- self.handlers_mut().push(EventHandler {
- handler: Rc::new(move |view, event, event_cx| {
- let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
- if event.button == button && !event.is_down {
- handler(view, event, event_cx);
- }
- }),
- event_type: TypeId::of::<MouseButtonEvent>(),
- outside_bounds: false,
- });
- self
- }
- fn mouse_up_outside(
- mut self,
- button: MouseButton,
- handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
- ) -> Self
- where
- Self: Sized,
- {
- self.handlers_mut().push(EventHandler {
- handler: Rc::new(move |view, event, event_cx| {
- let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
- if event.button == button && !event.is_down {
- handler(view, event, event_cx);
- }
- }),
- event_type: TypeId::of::<MouseButtonEvent>(),
- outside_bounds: true,
- });
- self
- }
- fn block(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().display = Some(Display::Block);
- self
- }
- fn flex(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().display = Some(Display::Flex);
- self
- }
- fn grid(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().display = Some(Display::Grid);
- self
- }
- fn overflow_visible(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow = OptionalPoint {
- x: Some(Overflow::Visible),
- y: Some(Overflow::Visible),
- };
- self
- }
- fn overflow_hidden(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow = OptionalPoint {
- x: Some(Overflow::Hidden),
- y: Some(Overflow::Hidden),
- };
- self
- }
- fn overflow_scroll(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow = OptionalPoint {
- x: Some(Overflow::Scroll),
- y: Some(Overflow::Scroll),
- };
- self
- }
- fn overflow_x_visible(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow.x = Some(Overflow::Visible);
- self
- }
- fn overflow_x_hidden(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow.x = Some(Overflow::Hidden);
- self
- }
- fn overflow_x_scroll(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow.x = Some(Overflow::Scroll);
- self
- }
- fn overflow_y_visible(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow.y = Some(Overflow::Visible);
- self
- }
- fn overflow_y_hidden(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow.y = Some(Overflow::Hidden);
- self
- }
- fn overflow_y_scroll(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().overflow.y = Some(Overflow::Scroll);
- self
- }
- fn relative(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().position = Some(Position::Relative);
- self
- }
- fn absolute(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().position = Some(Position::Absolute);
- self
- }
- fn inset_0(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(0.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_px(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(1.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_0_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.125).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.25).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.375).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.5).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.625).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_3(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.75).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_3_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.875).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_4(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.25).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_6(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.5).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_7(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.75).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_8(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_9(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.25).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_10(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.5).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_11(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.75).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_12(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_14(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.5).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_16(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(4.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_20(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(5.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_24(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(6.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_28(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(7.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_32(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(8.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_36(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(9.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_40(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(10.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_44(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(11.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_48(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(12.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_52(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(13.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_56(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(14.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_60(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(15.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_64(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(16.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_72(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(18.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_80(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(20.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_96(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(24.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_half(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_3_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(20.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(40.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_3_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(60.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_4_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(80.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_3_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_4_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_5_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_1_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(8.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_2_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_3_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_4_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_5_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(41.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_6_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_7_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(58.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_8_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_9_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_10_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_11_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(91.666667).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn inset_full(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(100.).into();
- {
- let inset = self
- .computed_style()
- .inset
- .get_or_insert_with(Default::default);
- inset.top = length;
- inset.right = length;
- inset.bottom = length;
- inset.left = length;
- self
- }
- }
- fn w(mut self, width: impl Into<Length>) -> Self
- where
- Self: Sized,
- {
- self.declared_style().size.width = Some(width.into());
- self
- }
- fn w_auto(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().size.width = Some(Length::Auto);
- self
- }
- fn w_0(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(0.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_px(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(1.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_0_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.125).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.25).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.375).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.5).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.625).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_3(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.75).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_3_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.875).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_4(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.25).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_6(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.5).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_7(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.75).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_8(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_9(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.25).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_10(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.5).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_11(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.75).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_12(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_14(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.5).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_16(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(4.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_20(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(5.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_24(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(6.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_28(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(7.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_32(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(8.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_36(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(9.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_40(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(10.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_44(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(11.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_48(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(12.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_52(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(13.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_56(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(14.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_60(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(15.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_64(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(16.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_72(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(18.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_80(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(20.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_96(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(24.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_half(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_3_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(20.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(40.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_3_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(60.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_4_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(80.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_3_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_4_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_5_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_1_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(8.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_2_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_3_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_4_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_5_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(41.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_6_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_7_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(58.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_8_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_9_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_10_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_11_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(91.666667).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn w_full(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(100.).into();
- {
- self.declared_style().size.width = Some(length);
- self
- }
- }
- fn min_w_0(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(0.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_px(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(1.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_0_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.125).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.25).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.375).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.5).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.625).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_3(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.75).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_3_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.875).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_4(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.25).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_6(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.5).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_7(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.75).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_8(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_9(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.25).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_10(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.5).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_11(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.75).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_12(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_14(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.5).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_16(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(4.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_20(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(5.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_24(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(6.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_28(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(7.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_32(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(8.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_36(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(9.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_40(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(10.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_44(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(11.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_48(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(12.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_52(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(13.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_56(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(14.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_60(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(15.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_64(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(16.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_72(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(18.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_80(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(20.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_96(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(24.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_half(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_3_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(20.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(40.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_3_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(60.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_4_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(80.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_3_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_4_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_5_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_1_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(8.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_2_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_3_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_4_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_5_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(41.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_6_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_7_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(58.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_8_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_9_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_10_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_11_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(91.666667).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn min_w_full(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(100.).into();
- {
- self.declared_style().min_size.width = Some(length);
- self
- }
- }
- fn h(mut self, height: impl Into<Length>) -> Self
- where
- Self: Sized,
- {
- self.declared_style().size.height = Some(height.into());
- self
- }
- fn h_auto(mut self) -> Self
- where
- Self: Sized,
- {
- self.declared_style().size.height = Some(Length::Auto);
- self
- }
- fn h_0(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Pixels(0.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_px(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Pixels(1.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_0_5(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.125).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.25).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1_5(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.375).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.5).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2_5(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.625).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_3(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.75).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_3_5(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(0.875).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_4(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(1.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_5(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(1.25).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_6(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(1.5).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_7(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(1.75).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_8(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(2.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_9(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(2.25).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_10(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(2.5).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_11(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(2.75).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_12(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(3.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_14(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(3.5).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_16(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(4.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_20(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(5.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_24(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(6.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_28(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(7.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_32(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(8.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_36(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(9.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_40(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(10.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_44(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(11.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_48(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(12.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_52(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(13.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_56(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(14.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_60(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(15.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_64(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(16.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_72(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(18.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_80(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(20.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_96(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Rems(24.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_half(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(25.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_3_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(75.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(20.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(40.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_3_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(60.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_4_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(80.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_3_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_4_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_5_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_1_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(8.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_2_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_3_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(25.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_4_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_5_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(41.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_6_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(50.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_7_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(58.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_8_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_9_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(75.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_10_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_11_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(91.666667).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn h_full(mut self) -> Self
- where
- Self: Sized,
- {
- let height = DefinedLength::Percent(100.).into();
- {
- self.declared_style().size.height = Some(height);
- self
- }
- }
- fn min_h_0(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(0.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_px(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Pixels(1.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_0_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.125).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.25).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.375).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.5).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.625).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_3(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.75).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_3_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(0.875).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_4(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_5(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.25).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_6(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.5).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_7(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(1.75).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_8(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_9(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.25).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_10(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.5).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_11(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(2.75).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_12(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_14(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(3.5).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_16(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(4.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_20(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(5.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_24(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(6.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_28(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(7.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_32(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(8.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_36(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(9.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_40(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(10.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_44(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(11.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_48(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(12.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_52(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(13.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_56(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(14.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_60(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(15.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_64(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(16.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_72(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(18.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_80(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(20.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_96(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Rems(24.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_half(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2_3rd(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_3_4th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(20.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(40.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_3_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(60.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_4_5th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(80.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_3_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_4_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_5_6th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_1_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(8.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_2_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(16.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_3_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(25.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_4_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(33.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_5_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(41.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_6_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(50.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_7_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(58.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_8_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(66.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_9_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(75.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_10_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(83.333333).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_11_12th(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(91.666667).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn min_h_full(mut self) -> Self
- where
- Self: Sized,
- {
- let length = DefinedLength::Percent(100.).into();
- {
- self.declared_style().min_size.height = Some(length);
- self
- }
- }
- fn hoverable(self) -> Hoverable<V, Self>
- where
- Self: Sized,
- {
- Hoverable::new(self)
- }
- fn fill(mut self, fill: impl Into<Fill>) -> Self
- where
- Self: Sized,
- {
- self.declared_style().fill = Some(Some(fill.into()));
- self
- }
- fn text_color(mut self, color: impl Into<Hsla>) -> Self
- where
- Self: Sized,
- {
- self.declared_style().text_color = Some(Some(color.into()));
- self
- }
- }
- trait ElementObject<V> {
- fn style(&mut self) -> &mut OptionalStyle;
- fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> Result<(NodeId, Box<dyn Any>)>;
- fn paint(
- &mut self,
- layout: Layout<dyn Any>,
- view: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Result<()>;
- }
- impl<V: 'static, E: Element<V>> ElementObject<V> for E {
- fn style(&mut self) -> &mut OptionalStyle {
- Element::declared_style(self)
- }
- fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
- Element::handlers_mut(self)
- }
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> Result<(NodeId, Box<dyn Any>)> {
- let (node_id, layout) = self.layout(view, cx)?;
- let layout = Box::new(layout) as Box<dyn Any>;
- Ok((node_id, layout))
- }
- fn paint(
- &mut self,
- layout: Layout<dyn Any>,
- view: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Result<()> {
- let layout = Layout {
- from_engine: layout.from_engine,
- from_element: layout.from_element.downcast_mut::<E::Layout>().unwrap(),
- };
- self.paint(layout, view, cx)
- }
- }
- /// A dynamically typed element.
- pub struct AnyElement<V> {
- element: Box<dyn ElementObject<V>>,
- layout: Option<(NodeId, Box<dyn Any>)>,
- }
- impl<V: 'static> AnyElement<V> {
- pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<NodeId> {
- let pushed_text_style = self.push_text_style(cx);
- let (node_id, layout) = self.element.layout(view, cx)?;
- self.layout = Some((node_id, layout));
- if pushed_text_style {
- cx.pop_text_style();
- }
- Ok(node_id)
- }
- pub fn push_text_style(&mut self, cx: &mut impl RenderContext) -> bool {
- let text_style = self.element.style().text_style();
- if let Some(text_style) = text_style {
- let mut current_text_style = cx.text_style();
- text_style.apply(&mut current_text_style);
- cx.push_text_style(current_text_style);
- true
- } else {
- false
- }
- }
- pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
- let pushed_text_style = self.push_text_style(cx);
- let (layout_node_id, element_layout) =
- self.layout.as_mut().expect("paint called before layout");
- let layout = Layout {
- from_engine: cx
- .layout_engine()
- .unwrap()
- .computed_layout(*layout_node_id)
- .expect("make sure you're using this within a gpui2 adapter element"),
- from_element: element_layout.as_mut(),
- };
- let style = self.element.style();
- let fill_color = style.fill.flatten().and_then(|fill| fill.color());
- if let Some(fill_color) = fill_color {
- cx.scene.push_quad(gpui::scene::Quad {
- bounds: layout.from_engine.bounds,
- background: Some(fill_color.into()),
- border: Default::default(),
- corner_radii: Default::default(),
- });
- }
- for event_handler in self.element.handlers_mut().iter().cloned() {
- let EngineLayout { order, bounds } = layout.from_engine;
- let view_id = cx.view_id();
- let view_event_handler = event_handler.handler.clone();
- cx.scene
- .interactive_regions
- .push(gpui::scene::InteractiveRegion {
- order,
- bounds,
- outside_bounds: event_handler.outside_bounds,
- event_handler: Rc::new(move |view, event, window_cx, view_id| {
- let mut view_context = ViewContext::mutable(window_cx, view_id);
- let mut event_context = EventContext::new(&mut view_context);
- view_event_handler(
- view.downcast_mut().unwrap(),
- event,
- &mut event_context,
- );
- }),
- event_type: event_handler.event_type,
- view_id,
- });
- }
- self.element.paint(layout, view, cx)?;
- if pushed_text_style {
- cx.pop_text_style();
- }
- Ok(())
- }
- }
- impl<V: 'static> Element<V> for AnyElement<V> {
- type Layout = ();
- fn declared_style(&mut self) -> &mut OptionalStyle {
- self.element.style()
- }
- fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
- self.element.handlers_mut()
- }
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> Result<(NodeId, Self::Layout)> {
- Ok((self.layout(view, cx)?, ()))
- }
- fn paint(
- &mut self,
- layout: Layout<()>,
- view: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Result<()> {
- self.paint(view, cx)
- }
- }
- pub trait IntoElement<V: 'static> {
- type Element: Element<V>;
- fn into_element(self) -> Self::Element;
- fn into_any_element(self) -> AnyElement<V>
- where
- Self: Sized,
- {
- self.into_element().into_any()
- }
- }
-}
-mod frame {
- use crate::{
- element::{
- AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId,
- PaintContext,
- },
- style::{OptionalStyle, Style},
- };
- use anyhow::{anyhow, Result};
- use gpui::LayoutNodeId;
- use gpui_macros::IntoElement;
- #[element_crate = "crate"]
- pub struct Frame<V: 'static> {
- style: OptionalStyle,
- handlers: Vec<EventHandler<V>>,
- children: Vec<AnyElement<V>>,
- }
- impl<V: 'static> crate::element::IntoElement<V> for Frame<V> {
- type Element = Self;
- fn into_element(self) -> Self {
- self
- }
- }
- pub fn frame<V>() -> Frame<V> {
- Frame {
- style: OptionalStyle::default(),
- handlers: Vec::new(),
- children: Vec::new(),
- }
- }
- impl<V: 'static> Element<V> for Frame<V> {
- type Layout = ();
- fn declared_style(&mut self) -> &mut OptionalStyle {
- &mut self.style
- }
- fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
- &mut self.handlers
- }
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> Result<(NodeId, Self::Layout)> {
- let child_layout_node_ids = self
- .children
- .iter_mut()
- .map(|child| child.layout(view, cx))
- .collect::<Result<Vec<LayoutNodeId>>>()?;
- let rem_size = cx.rem_pixels();
- let style: Style = self.style.into();
- let node_id = cx
- .layout_engine()
- .ok_or_else(|| {
- ::anyhow::__private::must_use({
- let error =
- ::anyhow::__private::format_err(format_args!("no layout engine"));
- error
- })
- })?
- .add_node(style.to_taffy(rem_size), child_layout_node_ids)?;
- Ok((node_id, ()))
- }
- fn paint(
- &mut self,
- layout: Layout<()>,
- view: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Result<()> {
- for child in &mut self.children {
- child.paint(view, cx)?;
- }
- Ok(())
- }
- }
- impl<V: 'static> Frame<V> {
- pub fn child(mut self, child: impl IntoElement<V>) -> Self {
- self.children.push(child.into_any_element());
- self
- }
- pub fn children<I, E>(mut self, children: I) -> Self
- where
- I: IntoIterator<Item = E>,
- E: IntoElement<V>,
- {
- self.children
- .extend(children.into_iter().map(|e| e.into_any_element()));
- self
- }
- }
-}
-mod hoverable {
- use crate::{
- element::Element,
- style::{OptionalStyle, Style},
- };
- use gpui::{
- geometry::{rect::RectF, vector::Vector2F},
- scene::MouseMove,
- EngineLayout,
- };
- use std::{cell::Cell, marker::PhantomData, rc::Rc};
- pub struct Hoverable<V, E> {
- hover_style: OptionalStyle,
- computed_style: Option<Style>,
- view_type: PhantomData<V>,
- child: E,
- }
- impl<V, E> Hoverable<V, E> {
- pub fn new(child: E) -> Self {
- Self {
- hover_style: OptionalStyle::default(),
- computed_style: None,
- view_type: PhantomData,
- child,
- }
- }
- }
- impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
- type Layout = E::Layout;
- fn declared_style(&mut self) -> &mut OptionalStyle {
- &mut self.hover_style
- }
- fn computed_style(&mut self) -> &OptionalStyle {
- ::core::panicking::panic("not yet implemented")
- }
- fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
- self.child.handlers_mut()
- }
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut gpui::LayoutContext<V>,
- ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
- self.child.layout(view, cx)
- }
- fn paint<'a>(
- &mut self,
- layout: crate::element::Layout<Self::Layout>,
- view: &mut V,
- cx: &mut crate::element::PaintContext<V>,
- ) -> anyhow::Result<()> {
- let EngineLayout { bounds, order } = layout.from_engine;
- let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
- let was_hovered = Rc::new(Cell::new(false));
- self.child.paint(layout, view, cx)?;
- cx.draw_interactive_region(
- order,
- window_bounds,
- false,
- move |view, event: &MouseMove, cx| {
- let is_hovered = bounds.contains_point(cx.mouse_position());
- if is_hovered != was_hovered.get() {
- was_hovered.set(is_hovered);
- cx.repaint();
- }
- },
- );
- Ok(())
- }
- }
-}
-mod paint_context {
- use derive_more::{Deref, DerefMut};
- use gpui::{geometry::rect::RectF, EventContext, RenderContext, ViewContext};
- pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
- use std::{any::TypeId, rc::Rc};
- pub use taffy::tree::NodeId;
- pub struct PaintContext<'a, 'b, 'c, 'd, V> {
- #[deref]
- #[deref_mut]
- pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
- pub(crate) scene: &'d mut gpui::SceneBuilder,
- }
- impl<'a, 'b, 'c, 'd, V> ::core::ops::Deref for PaintContext<'a, 'b, 'c, 'd, V> {
- type Target = &'d mut LegacyPaintContext<'a, 'b, 'c, V>;
- #[inline]
- fn deref(&self) -> &Self::Target {
- &self.legacy_cx
- }
- }
- impl<'a, 'b, 'c, 'd, V> ::core::ops::DerefMut for PaintContext<'a, 'b, 'c, 'd, V> {
- #[inline]
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.legacy_cx
- }
- }
- impl<V> RenderContext for PaintContext<'_, '_, '_, '_, V> {
- fn text_style(&self) -> gpui::fonts::TextStyle {
- self.legacy_cx.text_style()
- }
- fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
- self.legacy_cx.push_text_style(style)
- }
- fn pop_text_style(&mut self) {
- self.legacy_cx.pop_text_style()
- }
- }
- impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
- pub fn new(
- legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
- scene: &'d mut gpui::SceneBuilder,
- ) -> Self {
- Self { legacy_cx, scene }
- }
- pub fn draw_interactive_region<E: 'static>(
- &mut self,
- order: u32,
- bounds: RectF,
- outside_bounds: bool,
- event_handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
- ) {
- self.scene
- .interactive_regions
- .push(gpui::scene::InteractiveRegion {
- order,
- bounds,
- outside_bounds,
- event_handler: Rc::new(move |view, event, window_cx, view_id| {
- let mut view_context = ViewContext::mutable(window_cx, view_id);
- let mut event_context = EventContext::new(&mut view_context);
- event_handler(
- view.downcast_mut().unwrap(),
- event.downcast_ref().unwrap(),
- &mut event_context,
- );
- }),
- event_type: TypeId::of::<E>(),
- view_id: self.view_id(),
- });
- }
- }
-}
-mod style {
- use crate::color::Hsla;
- use gpui::geometry::{
- DefinedLength, Edges, Length, OptionalEdges, OptionalPoint, OptionalSize, Point, Size,
- };
- use optional::Optional;
- pub use taffy::style::{
- AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
- Overflow, Position,
- };
- pub struct Style {
- /// What layout strategy should be used?
- pub display: Display,
- /// How children overflowing their container should affect layout
- #[optional]
- pub overflow: Point<Overflow>,
- /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
- pub scrollbar_width: f32,
- /// What should the `position` value of this struct use as a base offset?
- pub position: Position,
- /// How should the position of this element be tweaked relative to the layout defined?
- pub inset: Edges<Length>,
- /// Sets the initial size of the item
- #[optional]
- pub size: Size<Length>,
- /// Controls the minimum size of the item
- #[optional]
- pub min_size: Size<Length>,
- /// Controls the maximum size of the item
- #[optional]
- pub max_size: Size<Length>,
- /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
- pub aspect_ratio: Option<f32>,
- /// How large should the margin be on each side?
- #[optional]
- pub margin: Edges<Length>,
- /// How large should the padding be on each side?
- pub padding: Edges<DefinedLength>,
- /// How large should the border be on each side?
- pub border: Edges<DefinedLength>,
- /// How this node's children aligned in the cross/block axis?
- pub align_items: Option<AlignItems>,
- /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
- pub align_self: Option<AlignSelf>,
- /// How should content contained within this item be aligned in the cross/block axis
- pub align_content: Option<AlignContent>,
- /// How should contained within this item be aligned in the main/inline axis
- pub justify_content: Option<JustifyContent>,
- /// How large should the gaps between items in a flex container be?
- pub gap: Size<DefinedLength>,
- /// Which direction does the main axis flow in?
- pub flex_direction: FlexDirection,
- /// Should elements wrap, or stay in a single line?
- pub flex_wrap: FlexWrap,
- /// Sets the initial main axis size of the item
- pub flex_basis: Length,
- /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
- pub flex_grow: f32,
- /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
- pub flex_shrink: f32,
- /// The fill color of this element
- pub fill: Option<Fill>,
- /// The color of text within this element. Cascades to children unless overridden.
- pub text_color: Option<Hsla>,
- }
- #[automatically_derived]
- impl ::core::clone::Clone for Style {
- #[inline]
- fn clone(&self) -> Style {
- Style {
- display: ::core::clone::Clone::clone(&self.display),
- overflow: ::core::clone::Clone::clone(&self.overflow),
- scrollbar_width: ::core::clone::Clone::clone(&self.scrollbar_width),
- position: ::core::clone::Clone::clone(&self.position),
- inset: ::core::clone::Clone::clone(&self.inset),
- size: ::core::clone::Clone::clone(&self.size),
- min_size: ::core::clone::Clone::clone(&self.min_size),
- max_size: ::core::clone::Clone::clone(&self.max_size),
- aspect_ratio: ::core::clone::Clone::clone(&self.aspect_ratio),
- margin: ::core::clone::Clone::clone(&self.margin),
- padding: ::core::clone::Clone::clone(&self.padding),
- border: ::core::clone::Clone::clone(&self.border),
- align_items: ::core::clone::Clone::clone(&self.align_items),
- align_self: ::core::clone::Clone::clone(&self.align_self),
- align_content: ::core::clone::Clone::clone(&self.align_content),
- justify_content: ::core::clone::Clone::clone(&self.justify_content),
- gap: ::core::clone::Clone::clone(&self.gap),
- flex_direction: ::core::clone::Clone::clone(&self.flex_direction),
- flex_wrap: ::core::clone::Clone::clone(&self.flex_wrap),
- flex_basis: ::core::clone::Clone::clone(&self.flex_basis),
- flex_grow: ::core::clone::Clone::clone(&self.flex_grow),
- flex_shrink: ::core::clone::Clone::clone(&self.flex_shrink),
- fill: ::core::clone::Clone::clone(&self.fill),
- text_color: ::core::clone::Clone::clone(&self.text_color),
- }
- }
- }
- pub struct OptionalStyle {
- pub display: Option<Display>,
- pub overflow: OptionalPoint<Overflow>,
- pub scrollbar_width: Option<f32>,
- pub position: Option<Position>,
- pub inset: Option<Edges<Length>>,
- pub size: OptionalSize<Length>,
- pub min_size: OptionalSize<Length>,
- pub max_size: OptionalSize<Length>,
- pub aspect_ratio: Option<Option<f32>>,
- pub margin: OptionalEdges<Length>,
- pub padding: Option<Edges<DefinedLength>>,
- pub border: Option<Edges<DefinedLength>>,
- pub align_items: Option<Option<AlignItems>>,
- pub align_self: Option<Option<AlignSelf>>,
- pub align_content: Option<Option<AlignContent>>,
- pub justify_content: Option<Option<JustifyContent>>,
- pub gap: Option<Size<DefinedLength>>,
- pub flex_direction: Option<FlexDirection>,
- pub flex_wrap: Option<FlexWrap>,
- pub flex_basis: Option<Length>,
- pub flex_grow: Option<f32>,
- pub flex_shrink: Option<f32>,
- pub fill: Option<Option<Fill>>,
- pub text_color: Option<Option<Hsla>>,
- }
- #[automatically_derived]
- impl ::core::default::Default for OptionalStyle {
- #[inline]
- fn default() -> OptionalStyle {
- OptionalStyle {
- display: ::core::default::Default::default(),
- overflow: ::core::default::Default::default(),
- scrollbar_width: ::core::default::Default::default(),
- position: ::core::default::Default::default(),
- inset: ::core::default::Default::default(),
- size: ::core::default::Default::default(),
- min_size: ::core::default::Default::default(),
- max_size: ::core::default::Default::default(),
- aspect_ratio: ::core::default::Default::default(),
- margin: ::core::default::Default::default(),
- padding: ::core::default::Default::default(),
- border: ::core::default::Default::default(),
- align_items: ::core::default::Default::default(),
- align_self: ::core::default::Default::default(),
- align_content: ::core::default::Default::default(),
- justify_content: ::core::default::Default::default(),
- gap: ::core::default::Default::default(),
- flex_direction: ::core::default::Default::default(),
- flex_wrap: ::core::default::Default::default(),
- flex_basis: ::core::default::Default::default(),
- flex_grow: ::core::default::Default::default(),
- flex_shrink: ::core::default::Default::default(),
- fill: ::core::default::Default::default(),
- text_color: ::core::default::Default::default(),
- }
- }
- }
- #[automatically_derived]
- impl ::core::clone::Clone for OptionalStyle {
- #[inline]
- fn clone(&self) -> OptionalStyle {
- OptionalStyle {
- display: ::core::clone::Clone::clone(&self.display),
- overflow: ::core::clone::Clone::clone(&self.overflow),
- scrollbar_width: ::core::clone::Clone::clone(&self.scrollbar_width),
- position: ::core::clone::Clone::clone(&self.position),
- inset: ::core::clone::Clone::clone(&self.inset),
- size: ::core::clone::Clone::clone(&self.size),
- min_size: ::core::clone::Clone::clone(&self.min_size),
- max_size: ::core::clone::Clone::clone(&self.max_size),
- aspect_ratio: ::core::clone::Clone::clone(&self.aspect_ratio),
- margin: ::core::clone::Clone::clone(&self.margin),
- padding: ::core::clone::Clone::clone(&self.padding),
- border: ::core::clone::Clone::clone(&self.border),
- align_items: ::core::clone::Clone::clone(&self.align_items),
- align_self: ::core::clone::Clone::clone(&self.align_self),
- align_content: ::core::clone::Clone::clone(&self.align_content),
- justify_content: ::core::clone::Clone::clone(&self.justify_content),
- gap: ::core::clone::Clone::clone(&self.gap),
- flex_direction: ::core::clone::Clone::clone(&self.flex_direction),
- flex_wrap: ::core::clone::Clone::clone(&self.flex_wrap),
- flex_basis: ::core::clone::Clone::clone(&self.flex_basis),
- flex_grow: ::core::clone::Clone::clone(&self.flex_grow),
- flex_shrink: ::core::clone::Clone::clone(&self.flex_shrink),
- fill: ::core::clone::Clone::clone(&self.fill),
- text_color: ::core::clone::Clone::clone(&self.text_color),
- }
- }
- }
- impl Optional for OptionalStyle {
- type Base = Style;
- fn assign(&self, base: &mut Self::Base) {
- if let Some(value) = self.display.clone() {
- base.display = value;
- }
- if let Some(value) = self.overflow.clone() {
- base.overflow = value;
- }
- if let Some(value) = self.scrollbar_width.clone() {
- base.scrollbar_width = value;
- }
- if let Some(value) = self.position.clone() {
- base.position = value;
- }
- if let Some(value) = self.inset.clone() {
- base.inset = value;
- }
- if let Some(value) = self.size.clone() {
- base.size = value;
- }
- if let Some(value) = self.min_size.clone() {
- base.min_size = value;
- }
- if let Some(value) = self.max_size.clone() {
- base.max_size = value;
- }
- if let Some(value) = self.aspect_ratio.clone() {
- base.aspect_ratio = value;
- }
- if let Some(value) = self.margin.clone() {
- base.margin = value;
- }
- if let Some(value) = self.padding.clone() {
- base.padding = value;
- }
- if let Some(value) = self.border.clone() {
- base.border = value;
- }
- if let Some(value) = self.align_items.clone() {
- base.align_items = value;
- }
- if let Some(value) = self.align_self.clone() {
- base.align_self = value;
- }
- if let Some(value) = self.align_content.clone() {
- base.align_content = value;
- }
- if let Some(value) = self.justify_content.clone() {
- base.justify_content = value;
- }
- if let Some(value) = self.gap.clone() {
- base.gap = value;
- }
- if let Some(value) = self.flex_direction.clone() {
- base.flex_direction = value;
- }
- if let Some(value) = self.flex_wrap.clone() {
- base.flex_wrap = value;
- }
- if let Some(value) = self.flex_basis.clone() {
- base.flex_basis = value;
- }
- if let Some(value) = self.flex_grow.clone() {
- base.flex_grow = value;
- }
- if let Some(value) = self.flex_shrink.clone() {
- base.flex_shrink = value;
- }
- if let Some(value) = self.fill.clone() {
- base.fill = value;
- }
- if let Some(value) = self.text_color.clone() {
- base.text_color = value;
- }
- }
- }
- impl From<OptionalStyle> for Style
- where
- Style: Default,
- {
- fn from(wrapper: OptionalStyle) -> Self {
- let mut base = Self::default();
- wrapper.assign(&mut base);
- base
- }
- }
- impl Style {
- pub const DEFAULT: Style = Style {
- display: Display::DEFAULT,
- overflow: Point {
- x: Overflow::Visible,
- y: Overflow::Visible,
- },
- scrollbar_width: 0.0,
- position: Position::Relative,
- inset: Edges::auto(),
- margin: Edges::<Length>::zero(),
- padding: Edges::<DefinedLength>::zero(),
- border: Edges::<DefinedLength>::zero(),
- size: Size::auto(),
- min_size: Size::auto(),
- max_size: Size::auto(),
- aspect_ratio: None,
- gap: Size::zero(),
- align_items: None,
- align_self: None,
- align_content: None,
- justify_content: None,
- flex_direction: FlexDirection::Row,
- flex_wrap: FlexWrap::NoWrap,
- flex_grow: 0.0,
- flex_shrink: 1.0,
- flex_basis: Length::Auto,
- fill: None,
- text_color: None,
- };
- pub fn new() -> Self {
- Self::DEFAULT.clone()
- }
- pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
- taffy::style::Style {
- display: self.display,
- overflow: self.overflow.clone().into(),
- scrollbar_width: self.scrollbar_width,
- position: self.position,
- inset: self.inset.to_taffy(rem_size),
- size: self.size.to_taffy(rem_size),
- min_size: self.min_size.to_taffy(rem_size),
- max_size: self.max_size.to_taffy(rem_size),
- aspect_ratio: self.aspect_ratio,
- margin: self.margin.to_taffy(rem_size),
- padding: self.padding.to_taffy(rem_size),
- border: self.border.to_taffy(rem_size),
- align_items: self.align_items,
- align_self: self.align_self,
- align_content: self.align_content,
- justify_content: self.justify_content,
- gap: self.gap.to_taffy(rem_size),
- flex_direction: self.flex_direction,
- flex_wrap: self.flex_wrap,
- flex_basis: self.flex_basis.to_taffy(rem_size).into(),
- flex_grow: self.flex_grow,
- flex_shrink: self.flex_shrink,
- ..Default::default()
- }
- }
- }
- impl Default for Style {
- fn default() -> Self {
- Self::DEFAULT.clone()
- }
- }
- impl OptionalStyle {
- pub fn text_style(&self) -> Option<OptionalTextStyle> {
- self.text_color.map(|color| OptionalTextStyle { color })
- }
- }
- pub struct OptionalTextStyle {
- color: Option<Hsla>,
- }
- impl OptionalTextStyle {
- pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
- if let Some(color) = self.color {
- style.color = color.into();
- }
- }
- }
- pub enum Fill {
- Color(Hsla),
- }
- #[automatically_derived]
- impl ::core::clone::Clone for Fill {
- #[inline]
- fn clone(&self) -> Fill {
- match self {
- Fill::Color(__self_0) => Fill::Color(::core::clone::Clone::clone(__self_0)),
- }
- }
- }
- impl Fill {
- pub fn color(&self) -> Option<Hsla> {
- match self {
- Fill::Color(color) => Some(*color),
- }
- }
- }
- impl Default for Fill {
- fn default() -> Self {
- Self::Color(Hsla::default())
- }
- }
- impl From<Hsla> for Fill {
- fn from(color: Hsla) -> Self {
- Self::Color(color)
- }
- }
-}
-mod text {
- use crate::{
- element::{Element, ElementMetadata, EventHandler, IntoElement},
- style::Style,
- };
- use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
- use parking_lot::Mutex;
- use std::sync::Arc;
- impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
- type Element = Text<V>;
- fn into_element(self) -> Self::Element {
- Text {
- text: self.into(),
- metadata: Default::default(),
- }
- }
- }
- pub struct Text<V> {
- text: ArcCow<'static, str>,
- metadata: ElementMetadata<V>,
- }
- impl<V: 'static> Element<V> for Text<V> {
- type Layout = Arc<Mutex<Option<TextLayout>>>;
- fn declared_style(&mut self) -> &mut crate::style::OptionalStyle {
- &mut self.metadata.style
- }
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut gpui::LayoutContext<V>,
- ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
- let rem_size = cx.rem_pixels();
- let fonts = cx.platform().fonts();
- let text_style = cx.text_style();
- let line_height = cx.font_cache().line_height(text_style.font_size);
- let layout_engine = cx.layout_engine().expect("no layout engine present");
- let text = self.text.clone();
- let layout = Arc::new(Mutex::new(None));
- let style: Style = self.metadata.style.into();
- let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
- let layout = layout.clone();
- move |params| {
- let line_layout = fonts.layout_line(
- text.as_ref(),
- text_style.font_size,
- &[(text.len(), text_style.to_run())],
- );
- let size = Size {
- width: line_layout.width,
- height: line_height,
- };
- layout.lock().replace(TextLayout {
- line_layout: Arc::new(line_layout),
- line_height,
- });
- size
- }
- })?;
- Ok((node_id, layout))
- }
- fn paint<'a>(
- &mut self,
- layout: crate::element::Layout<Arc<Mutex<Option<TextLayout>>>>,
- view: &mut V,
- cx: &mut crate::element::PaintContext<V>,
- ) -> anyhow::Result<()> {
- let element_layout_lock = layout.from_element.lock();
- let element_layout = element_layout_lock
- .as_ref()
- .expect("layout has not been performed");
- let line_layout = element_layout.line_layout.clone();
- let line_height = element_layout.line_height;
- drop(element_layout_lock);
- let text_style = cx.text_style();
- let line = gpui::text_layout::Line::new(
- line_layout,
- &[(self.text.len(), text_style.to_run())],
- );
- line.paint(
- cx.scene,
- layout.from_engine.bounds.origin(),
- layout.from_engine.bounds,
- line_height,
- cx.legacy_cx,
- );
- Ok(())
- }
- fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
- &mut self.metadata.handlers
- }
- }
- pub struct TextLayout {
- line_layout: Arc<LineLayout>,
- line_height: f32,
- }
- pub enum ArcCow<'a, T: ?Sized> {
- Borrowed(&'a T),
- Owned(Arc<T>),
- }
- impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
- fn clone(&self) -> Self {
- match self {
- Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
- Self::Owned(owned) => Self::Owned(owned.clone()),
- }
- }
- }
- impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
- fn from(s: &'a T) -> Self {
- Self::Borrowed(s)
- }
- }
- impl<T> From<Arc<T>> for ArcCow<'_, T> {
- fn from(s: Arc<T>) -> Self {
- Self::Owned(s)
- }
- }
- impl From<String> for ArcCow<'_, str> {
- fn from(value: String) -> Self {
- Self::Owned(value.into())
- }
- }
- impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
- type Target = T;
- fn deref(&self) -> &Self::Target {
- match self {
- ArcCow::Borrowed(s) => s,
- ArcCow::Owned(s) => s.as_ref(),
- }
- }
- }
- impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
- fn as_ref(&self) -> &T {
- match self {
- ArcCow::Borrowed(borrowed) => borrowed,
- ArcCow::Owned(owned) => owned.as_ref(),
- }
- }
- }
-}
-mod themes {
- use crate::color::{Hsla, Lerp};
- use std::ops::Range;
- pub mod rose_pine {
- use crate::{
- color::{hsla, rgb, Hsla},
- ThemeColors,
- };
- use std::ops::Range;
- pub struct RosePineThemes {
- pub default: RosePinePalette,
- pub dawn: RosePinePalette,
- pub moon: RosePinePalette,
- }
- pub struct RosePinePalette {
- pub base: Hsla,
- pub surface: Hsla,
- pub overlay: Hsla,
- pub muted: Hsla,
- pub subtle: Hsla,
- pub text: Hsla,
- pub love: Hsla,
- pub gold: Hsla,
- pub rose: Hsla,
- pub pine: Hsla,
- pub foam: Hsla,
- pub iris: Hsla,
- pub highlight_low: Hsla,
- pub highlight_med: Hsla,
- pub highlight_high: Hsla,
- }
- #[automatically_derived]
- impl ::core::clone::Clone for RosePinePalette {
- #[inline]
- fn clone(&self) -> RosePinePalette {
- let _: ::core::clone::AssertParamIsClone<Hsla>;
- *self
- }
- }
- #[automatically_derived]
- impl ::core::marker::Copy for RosePinePalette {}
- #[automatically_derived]
- impl ::core::fmt::Debug for RosePinePalette {
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- let names: &'static _ = &[
- "base",
- "surface",
- "overlay",
- "muted",
- "subtle",
- "text",
- "love",
- "gold",
- "rose",
- "pine",
- "foam",
- "iris",
- "highlight_low",
- "highlight_med",
- "highlight_high",
- ];
- let values: &[&dyn::core::fmt::Debug] = &[
- &self.base,
- &self.surface,
- &self.overlay,
- &self.muted,
- &self.subtle,
- &self.text,
- &self.love,
- &self.gold,
- &self.rose,
- &self.pine,
- &self.foam,
- &self.iris,
- &self.highlight_low,
- &self.highlight_med,
- &&self.highlight_high,
- ];
- ::core::fmt::Formatter::debug_struct_fields_finish(
- f,
- "RosePinePalette",
- names,
- values,
- )
- }
- }
- impl RosePinePalette {
- pub fn default() -> RosePinePalette {
- RosePinePalette {
- base: rgb(0x191724),
- surface: rgb(0x1f1d2e),
- overlay: rgb(0x26233a),
- muted: rgb(0x6e6a86),
- subtle: rgb(0x908caa),
- text: rgb(0xe0def4),
- love: rgb(0xeb6f92),
- gold: rgb(0xf6c177),
- rose: rgb(0xebbcba),
- pine: rgb(0x31748f),
- foam: rgb(0x9ccfd8),
- iris: rgb(0xc4a7e7),
- highlight_low: rgb(0x21202e),
- highlight_med: rgb(0x403d52),
- highlight_high: rgb(0x524f67),
- }
- }
- pub fn moon() -> RosePinePalette {
- RosePinePalette {
- base: rgb(0x232136),
- surface: rgb(0x2a273f),
- overlay: rgb(0x393552),
- muted: rgb(0x6e6a86),
- subtle: rgb(0x908caa),
- text: rgb(0xe0def4),
- love: rgb(0xeb6f92),
- gold: rgb(0xf6c177),
- rose: rgb(0xea9a97),
- pine: rgb(0x3e8fb0),
- foam: rgb(0x9ccfd8),
- iris: rgb(0xc4a7e7),
- highlight_low: rgb(0x2a283e),
- highlight_med: rgb(0x44415a),
- highlight_high: rgb(0x56526e),
- }
- }
- pub fn dawn() -> RosePinePalette {
- RosePinePalette {
- base: rgb(0xfaf4ed),
- surface: rgb(0xfffaf3),
- overlay: rgb(0xf2e9e1),
- muted: rgb(0x9893a5),
- subtle: rgb(0x797593),
- text: rgb(0x575279),
- love: rgb(0xb4637a),
- gold: rgb(0xea9d34),
- rose: rgb(0xd7827e),
- pine: rgb(0x286983),
- foam: rgb(0x56949f),
- iris: rgb(0x907aa9),
- highlight_low: rgb(0xf4ede8),
- highlight_med: rgb(0xdfdad9),
- highlight_high: rgb(0xcecacd),
- }
- }
- }
- pub fn default() -> ThemeColors {
- theme_colors(&RosePinePalette::default())
- }
- pub fn moon() -> ThemeColors {
- theme_colors(&RosePinePalette::moon())
- }
- pub fn dawn() -> ThemeColors {
- theme_colors(&RosePinePalette::dawn())
- }
- fn theme_colors(p: &RosePinePalette) -> ThemeColors {
- ThemeColors {
- base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
- surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
- overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
- muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
- subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
- text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
- highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
- highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
- highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
- success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
- warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
- error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
- inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
- deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
- modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
- }
- }
- /// Produces a range by multiplying the saturation and lightness of the base color by the given
- /// start and end factors.
- fn scale_sl(
- base: Hsla,
- (start_s, start_l): (f32, f32),
- (end_s, end_l): (f32, f32),
- ) -> Range<Hsla> {
- let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
- let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
- Range { start, end }
- }
- }
- pub struct ThemeColors {
- pub base: Range<Hsla>,
- pub surface: Range<Hsla>,
- pub overlay: Range<Hsla>,
- pub muted: Range<Hsla>,
- pub subtle: Range<Hsla>,
- pub text: Range<Hsla>,
- pub highlight_low: Range<Hsla>,
- pub highlight_med: Range<Hsla>,
- pub highlight_high: Range<Hsla>,
- pub success: Range<Hsla>,
- pub warning: Range<Hsla>,
- pub error: Range<Hsla>,
- pub inserted: Range<Hsla>,
- pub deleted: Range<Hsla>,
- pub modified: Range<Hsla>,
- }
- impl ThemeColors {
- pub fn base(&self, level: f32) -> Hsla {
- self.base.lerp(level)
- }
- pub fn surface(&self, level: f32) -> Hsla {
- self.surface.lerp(level)
- }
- pub fn overlay(&self, level: f32) -> Hsla {
- self.overlay.lerp(level)
- }
- pub fn muted(&self, level: f32) -> Hsla {
- self.muted.lerp(level)
- }
- pub fn subtle(&self, level: f32) -> Hsla {
- self.subtle.lerp(level)
- }
- pub fn text(&self, level: f32) -> Hsla {
- self.text.lerp(level)
- }
- pub fn highlight_low(&self, level: f32) -> Hsla {
- self.highlight_low.lerp(level)
- }
- pub fn highlight_med(&self, level: f32) -> Hsla {
- self.highlight_med.lerp(level)
- }
- pub fn highlight_high(&self, level: f32) -> Hsla {
- self.highlight_high.lerp(level)
- }
- pub fn success(&self, level: f32) -> Hsla {
- self.success.lerp(level)
- }
- pub fn warning(&self, level: f32) -> Hsla {
- self.warning.lerp(level)
- }
- pub fn error(&self, level: f32) -> Hsla {
- self.error.lerp(level)
- }
- pub fn inserted(&self, level: f32) -> Hsla {
- self.inserted.lerp(level)
- }
- pub fn deleted(&self, level: f32) -> Hsla {
- self.deleted.lerp(level)
- }
- pub fn modified(&self, level: f32) -> Hsla {
- self.modified.lerp(level)
- }
- }
-}
-mod view {
- use crate::element::{AnyElement, Element};
- use gpui::{Element as _, ViewContext};
- pub fn view<F, E>(mut render: F) -> ViewFn
- where
- F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
- E: Element<ViewFn>,
- {
- ViewFn(Box::new(move |cx| (render)(cx).into_any()))
- }
- pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
- impl gpui::Entity for ViewFn {
- type Event = ();
- }
- impl gpui::View for ViewFn {
- fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
- (self.0)(cx).adapt().into_any()
- }
- }
-}
-fn main() {
- SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
- gpui::App::new(()).unwrap().run(|cx| {
- cx.add_window(
- WindowOptions {
- bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
- vec2f(0., 0.),
- vec2f(400., 300.),
- )),
- center: true,
- ..Default::default()
- },
- |_| view(|_| storybook(&rose_pine::moon())),
- );
- cx.platform().activate(true);
- });
-}
-fn storybook<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
- frame()
- .text_color(black())
- .h_full()
- .w_half()
- .fill(theme.success(0.5))
- .child(button().label("Hello").click(|_, _, _| {
- ::std::io::_print(format_args!("click!\n"));
- }))
-}