Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8589b96f28 | ||
![]() |
315b66fdf1 | ||
![]() |
1e9541c37d | ||
![]() |
910002d3f9 | ||
![]() |
d4f42e7fbc | ||
![]() |
f48847a7e7 | ||
![]() |
f938dad59e | ||
![]() |
576ba23ba8 | ||
![]() |
7bfa2a81f8 |
30 changed files with 438 additions and 198 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -9863,7 +9863,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.100.0"
|
||||
version = "0.100.1"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"ai",
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
// Whether to show the collaboration panel button in the status bar.
|
||||
"button": true,
|
||||
// Where to dock channels panel. Can be 'left' or 'right'.
|
||||
"dock": "right",
|
||||
"dock": "left",
|
||||
// Default width of the channels panel.
|
||||
"default_width": 240
|
||||
},
|
||||
|
|
|
@ -726,10 +726,10 @@ impl Panel for AssistantPanel {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
|
||||
fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
|
||||
match self.position(cx) {
|
||||
DockPosition::Left | DockPosition::Right => self.width = Some(size),
|
||||
DockPosition::Bottom => self.height = Some(size),
|
||||
DockPosition::Left | DockPosition::Right => self.width = size,
|
||||
DockPosition::Bottom => self.height = size,
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
|
|
@ -441,10 +441,12 @@ impl ChannelStore {
|
|||
|
||||
for channel in payload.channels {
|
||||
if let Some(existing_channel) = self.channels_by_id.get_mut(&channel.id) {
|
||||
// FIXME: We may be missing a path for this existing channel in certain cases
|
||||
let existing_channel = Arc::make_mut(existing_channel);
|
||||
existing_channel.name = channel.name;
|
||||
continue;
|
||||
}
|
||||
|
||||
self.channels_by_id.insert(
|
||||
channel.id,
|
||||
Arc::new(Channel {
|
||||
|
|
|
@ -3650,7 +3650,11 @@ impl Database {
|
|||
let ancestor_ids = self.get_channel_ancestors(id, tx).await?;
|
||||
let user_ids = channel_member::Entity::find()
|
||||
.distinct()
|
||||
.filter(channel_member::Column::ChannelId.is_in(ancestor_ids.iter().copied()))
|
||||
.filter(
|
||||
channel_member::Column::ChannelId
|
||||
.is_in(ancestor_ids.iter().copied())
|
||||
.and(channel_member::Column::Accepted.eq(true)),
|
||||
)
|
||||
.select_only()
|
||||
.column(channel_member::Column::UserId)
|
||||
.into_values::<_, QueryUserIds>()
|
||||
|
|
|
@ -770,6 +770,108 @@ async fn test_call_from_channel(
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_lost_channel_creation(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
deterministic.forbid_parking();
|
||||
let mut server = TestServer::start(&deterministic).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
|
||||
server
|
||||
.make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
|
||||
let channel_id = server.make_channel("x", (&client_a, cx_a), &mut []).await;
|
||||
|
||||
// Invite a member
|
||||
client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.invite_member(channel_id, client_b.user_id().unwrap(), false, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
deterministic.run_until_parked();
|
||||
|
||||
// Sanity check
|
||||
assert_channel_invitations(
|
||||
client_b.channel_store(),
|
||||
cx_b,
|
||||
&[ExpectedChannel {
|
||||
depth: 0,
|
||||
id: channel_id,
|
||||
name: "x".to_string(),
|
||||
user_is_admin: false,
|
||||
}],
|
||||
);
|
||||
|
||||
let subchannel_id = client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.create_channel("subchannel", Some(channel_id), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
deterministic.run_until_parked();
|
||||
|
||||
// Make sure A sees their new channel
|
||||
assert_channels(
|
||||
client_a.channel_store(),
|
||||
cx_a,
|
||||
&[
|
||||
ExpectedChannel {
|
||||
depth: 0,
|
||||
id: channel_id,
|
||||
name: "x".to_string(),
|
||||
user_is_admin: true,
|
||||
},
|
||||
ExpectedChannel {
|
||||
depth: 1,
|
||||
id: subchannel_id,
|
||||
name: "subchannel".to_string(),
|
||||
user_is_admin: true,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
// Accept the invite
|
||||
client_b
|
||||
.channel_store()
|
||||
.update(cx_b, |channel_store, _| {
|
||||
channel_store.respond_to_channel_invite(channel_id, true)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
deterministic.run_until_parked();
|
||||
|
||||
// B should now see the channel
|
||||
assert_channels(
|
||||
client_b.channel_store(),
|
||||
cx_b,
|
||||
&[
|
||||
ExpectedChannel {
|
||||
depth: 0,
|
||||
id: channel_id,
|
||||
name: "x".to_string(),
|
||||
user_is_admin: false,
|
||||
},
|
||||
ExpectedChannel {
|
||||
depth: 1,
|
||||
id: subchannel_id,
|
||||
name: "subchannel".to_string(),
|
||||
user_is_admin: false,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct ExpectedChannel {
|
||||
depth: usize,
|
||||
|
|
|
@ -86,7 +86,7 @@ impl_actions!(
|
|||
]
|
||||
);
|
||||
|
||||
const CHANNELS_PANEL_KEY: &'static str = "ChannelsPanel";
|
||||
const COLLABORATION_PANEL_KEY: &'static str = "CollaborationPanel";
|
||||
|
||||
pub fn init(_client: Arc<Client>, cx: &mut AppContext) {
|
||||
settings::register::<panel_settings::CollaborationPanelSettings>(cx);
|
||||
|
@ -397,7 +397,7 @@ impl CollabPanel {
|
|||
project: workspace.project().clone(),
|
||||
subscriptions: Vec::default(),
|
||||
match_candidates: Vec::default(),
|
||||
collapsed_sections: Vec::default(),
|
||||
collapsed_sections: vec![Section::Offline],
|
||||
workspace: workspace.weak_handle(),
|
||||
client: workspace.app_state().client.clone(),
|
||||
context_menu_on_selected: true,
|
||||
|
@ -464,7 +464,7 @@ impl CollabPanel {
|
|||
cx.spawn(|mut cx| async move {
|
||||
let serialized_panel = if let Some(panel) = cx
|
||||
.background()
|
||||
.spawn(async move { KEY_VALUE_STORE.read_kvp(CHANNELS_PANEL_KEY) })
|
||||
.spawn(async move { KEY_VALUE_STORE.read_kvp(COLLABORATION_PANEL_KEY) })
|
||||
.await
|
||||
.log_err()
|
||||
.flatten()
|
||||
|
@ -493,7 +493,7 @@ impl CollabPanel {
|
|||
async move {
|
||||
KEY_VALUE_STORE
|
||||
.write_kvp(
|
||||
CHANNELS_PANEL_KEY.into(),
|
||||
COLLABORATION_PANEL_KEY.into(),
|
||||
serde_json::to_string(&SerializedChannelsPanel { width })?,
|
||||
)
|
||||
.await?;
|
||||
|
@ -2354,7 +2354,7 @@ impl View for CollabPanel {
|
|||
.into_any()
|
||||
})
|
||||
.on_click(MouseButton::Left, |_, _, cx| cx.focus_self())
|
||||
.into_any_named("channels panel")
|
||||
.into_any_named("collaboration panel")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2391,8 +2391,8 @@ impl Panel for CollabPanel {
|
|||
.unwrap_or_else(|| settings::get::<CollaborationPanelSettings>(cx).default_width)
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
|
||||
self.width = Some(size);
|
||||
fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
|
||||
self.width = size;
|
||||
self.serialize(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -2404,7 +2404,10 @@ impl Panel for CollabPanel {
|
|||
}
|
||||
|
||||
fn icon_tooltip(&self) -> (String, Option<Box<dyn gpui::Action>>) {
|
||||
("Channels Panel".to_string(), Some(Box::new(ToggleFocus)))
|
||||
(
|
||||
"Collaboration Panel".to_string(),
|
||||
Some(Box::new(ToggleFocus)),
|
||||
)
|
||||
}
|
||||
|
||||
fn should_change_position_on_event(event: &Self::Event) -> bool {
|
||||
|
|
|
@ -9,8 +9,16 @@ mod sharing_status_indicator;
|
|||
|
||||
use call::{ActiveCall, Room};
|
||||
pub use collab_titlebar_item::CollabTitlebarItem;
|
||||
use gpui::{actions, AppContext, Task};
|
||||
use std::sync::Arc;
|
||||
use gpui::{
|
||||
actions,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
platform::{Screen, WindowBounds, WindowKind, WindowOptions},
|
||||
AppContext, Task,
|
||||
};
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
use util::ResultExt;
|
||||
use workspace::AppState;
|
||||
|
||||
|
@ -88,3 +96,29 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
|
|||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
fn notification_window_options(
|
||||
screen: Rc<dyn Screen>,
|
||||
window_size: Vector2F,
|
||||
) -> WindowOptions<'static> {
|
||||
const NOTIFICATION_PADDING: f32 = 16.;
|
||||
|
||||
let screen_bounds = screen.content_bounds();
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(
|
||||
screen_bounds.upper_right()
|
||||
+ vec2f(
|
||||
-NOTIFICATION_PADDING - window_size.x(),
|
||||
NOTIFICATION_PADDING,
|
||||
),
|
||||
window_size,
|
||||
)),
|
||||
titlebar: None,
|
||||
center: false,
|
||||
focus: false,
|
||||
show: true,
|
||||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
screen: Some(screen),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
use crate::notification_window_options;
|
||||
use call::{ActiveCall, IncomingCall};
|
||||
use client::proto;
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
|
||||
geometry::vector::vec2f,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AnyElement, AppContext, Entity, View, ViewContext, WindowHandle,
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use util::ResultExt;
|
||||
use workspace::AppState;
|
||||
|
||||
|
@ -23,31 +23,16 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
|||
}
|
||||
|
||||
if let Some(incoming_call) = incoming_call {
|
||||
const PADDING: f32 = 16.;
|
||||
let window_size = cx.read(|cx| {
|
||||
let theme = &theme::current(cx).incoming_call_notification;
|
||||
vec2f(theme.window_width, theme.window_height)
|
||||
});
|
||||
|
||||
for screen in cx.platform().screens() {
|
||||
let screen_bounds = screen.bounds();
|
||||
let window = cx.add_window(
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(
|
||||
screen_bounds.upper_right()
|
||||
- vec2f(PADDING + window_size.x(), PADDING),
|
||||
window_size,
|
||||
)),
|
||||
titlebar: None,
|
||||
center: false,
|
||||
focus: false,
|
||||
show: true,
|
||||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
screen: Some(screen),
|
||||
},
|
||||
|_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()),
|
||||
);
|
||||
let window = cx
|
||||
.add_window(notification_window_options(screen, window_size), |_| {
|
||||
IncomingCallNotification::new(incoming_call.clone(), app_state.clone())
|
||||
});
|
||||
|
||||
notification_windows.push(window);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::notification_window_options;
|
||||
use call::{room, ActiveCall};
|
||||
use client::User;
|
||||
use collections::HashMap;
|
||||
use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
|
||||
geometry::vector::vec2f,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AppContext, Entity, View, ViewContext,
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
@ -20,35 +21,19 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
|||
project_id,
|
||||
worktree_root_names,
|
||||
} => {
|
||||
const PADDING: f32 = 16.;
|
||||
let theme = &theme::current(cx).project_shared_notification;
|
||||
let window_size = vec2f(theme.window_width, theme.window_height);
|
||||
|
||||
for screen in cx.platform().screens() {
|
||||
let screen_bounds = screen.bounds();
|
||||
let window = cx.add_window(
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(
|
||||
screen_bounds.upper_right() - vec2f(PADDING + window_size.x(), PADDING),
|
||||
window_size,
|
||||
)),
|
||||
titlebar: None,
|
||||
center: false,
|
||||
focus: false,
|
||||
show: true,
|
||||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
screen: Some(screen),
|
||||
},
|
||||
|_| {
|
||||
let window =
|
||||
cx.add_window(notification_window_options(screen, window_size), |_| {
|
||||
ProjectSharedNotification::new(
|
||||
owner.clone(),
|
||||
*project_id,
|
||||
worktree_root_names.clone(),
|
||||
app_state.clone(),
|
||||
)
|
||||
},
|
||||
);
|
||||
});
|
||||
notification_windows
|
||||
.entry(*project_id)
|
||||
.or_insert(Vec::new())
|
||||
|
|
|
@ -577,6 +577,14 @@ impl AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn optional_global<T: 'static>(&self) -> Option<&T> {
|
||||
if let Some(global) = self.globals.get(&TypeId::of::<T>()) {
|
||||
Some(global.downcast_ref().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upgrade(&self) -> App {
|
||||
App(self.weak_self.as_ref().unwrap().upgrade().unwrap())
|
||||
}
|
||||
|
|
|
@ -186,16 +186,27 @@ pub trait Element<V: View>: 'static {
|
|||
Tooltip::new::<Tag>(id, text, action, style, self.into_any(), cx)
|
||||
}
|
||||
|
||||
fn resizable(
|
||||
/// Uses the the given element to calculate resizes for the given tag
|
||||
fn provide_resize_bounds<Tag: 'static>(self) -> BoundsProvider<V, Tag>
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
BoundsProvider::<_, Tag>::new(self.into_any())
|
||||
}
|
||||
|
||||
/// Calls the given closure with the new size of the element whenever the
|
||||
/// handle is dragged. This will be calculated in relation to the bounds
|
||||
/// provided by the given tag
|
||||
fn resizable<Tag: 'static>(
|
||||
self,
|
||||
side: HandleSide,
|
||||
size: f32,
|
||||
on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
|
||||
on_resize: impl 'static + FnMut(&mut V, Option<f32>, &mut ViewContext<V>),
|
||||
) -> Resizable<V>
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
Resizable::new(self.into_any(), side, size, on_resize)
|
||||
Resizable::new::<Tag>(self.into_any(), side, size, on_resize)
|
||||
}
|
||||
|
||||
fn mouse<Tag: 'static>(self, region_id: usize) -> MouseEventHandler<V>
|
||||
|
|
|
@ -82,6 +82,9 @@ impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
|
|||
view: &V,
|
||||
cx: &ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
element.debug(view, cx)
|
||||
serde_json::json!({
|
||||
"type": "ComponentAdapter",
|
||||
"child": element.debug(view, cx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use collections::HashMap;
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
geometry::rect::RectF,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::MouseDrag,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
AnyElement, AppContext, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, TypeTag, View, ViewContext,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -27,15 +27,6 @@ impl HandleSide {
|
|||
}
|
||||
}
|
||||
|
||||
/// 'before' is in reference to the standard english document ordering of left-to-right
|
||||
/// then top-to-bottom
|
||||
fn before_content(self) -> bool {
|
||||
match self {
|
||||
HandleSide::Left | HandleSide::Top => true,
|
||||
HandleSide::Right | HandleSide::Bottom => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn relevant_component(&self, vector: Vector2F) -> f32 {
|
||||
match self.axis() {
|
||||
Axis::Horizontal => vector.x(),
|
||||
|
@ -43,14 +34,6 @@ impl HandleSide {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_delta(&self, e: MouseDrag) -> f32 {
|
||||
if self.before_content() {
|
||||
self.relevant_component(e.prev_mouse_position) - self.relevant_component(e.position)
|
||||
} else {
|
||||
self.relevant_component(e.position) - self.relevant_component(e.prev_mouse_position)
|
||||
}
|
||||
}
|
||||
|
||||
fn of_rect(&self, bounds: RectF, handle_size: f32) -> RectF {
|
||||
match self {
|
||||
HandleSide::Top => RectF::new(bounds.origin(), vec2f(bounds.width(), handle_size)),
|
||||
|
@ -69,21 +52,29 @@ impl HandleSide {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_bounds(tag: TypeTag, cx: &AppContext) -> Option<&(RectF, RectF)>
|
||||
where
|
||||
{
|
||||
cx.optional_global::<ProviderMap>()
|
||||
.and_then(|map| map.0.get(&tag))
|
||||
}
|
||||
|
||||
pub struct Resizable<V: View> {
|
||||
child: AnyElement<V>,
|
||||
tag: TypeTag,
|
||||
handle_side: HandleSide,
|
||||
handle_size: f32,
|
||||
on_resize: Rc<RefCell<dyn FnMut(&mut V, f32, &mut ViewContext<V>)>>,
|
||||
on_resize: Rc<RefCell<dyn FnMut(&mut V, Option<f32>, &mut ViewContext<V>)>>,
|
||||
}
|
||||
|
||||
const DEFAULT_HANDLE_SIZE: f32 = 4.0;
|
||||
|
||||
impl<V: View> Resizable<V> {
|
||||
pub fn new(
|
||||
pub fn new<Tag: 'static>(
|
||||
child: AnyElement<V>,
|
||||
handle_side: HandleSide,
|
||||
size: f32,
|
||||
on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
|
||||
on_resize: impl 'static + FnMut(&mut V, Option<f32>, &mut ViewContext<V>),
|
||||
) -> Self {
|
||||
let child = match handle_side.axis() {
|
||||
Axis::Horizontal => child.constrained().with_max_width(size),
|
||||
|
@ -94,6 +85,7 @@ impl<V: View> Resizable<V> {
|
|||
Self {
|
||||
child,
|
||||
handle_side,
|
||||
tag: TypeTag::new::<Tag>(),
|
||||
handle_size: DEFAULT_HANDLE_SIZE,
|
||||
on_resize: Rc::new(RefCell::new(on_resize)),
|
||||
}
|
||||
|
@ -139,6 +131,14 @@ impl<V: View> Element<V> for Resizable<V> {
|
|||
handle_region,
|
||||
)
|
||||
.on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere
|
||||
.on_click(MouseButton::Left, {
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |click, v, cx| {
|
||||
if click.click_count == 2 {
|
||||
on_resize.borrow_mut()(v, None, cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on_drag(MouseButton::Left, {
|
||||
let bounds = bounds.clone();
|
||||
let side = self.handle_side;
|
||||
|
@ -146,16 +146,30 @@ impl<V: View> Element<V> for Resizable<V> {
|
|||
let min_size = side.relevant_component(constraint.min);
|
||||
let max_size = side.relevant_component(constraint.max);
|
||||
let on_resize = self.on_resize.clone();
|
||||
let tag = self.tag;
|
||||
move |event, view: &mut V, cx| {
|
||||
if event.end {
|
||||
return;
|
||||
}
|
||||
let new_size = min_size
|
||||
.max(prev_size + side.compute_delta(event))
|
||||
.min(max_size)
|
||||
.round();
|
||||
|
||||
let Some((bounds, _)) = get_bounds(tag, cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let new_size_raw = match side {
|
||||
// Handle on top side of element => Element is on bottom
|
||||
HandleSide::Top => bounds.height() + bounds.origin_y() - event.position.y(),
|
||||
// Handle on right side of element => Element is on left
|
||||
HandleSide::Right => event.position.x() - bounds.lower_left().x(),
|
||||
// Handle on left side of element => Element is on the right
|
||||
HandleSide::Left => bounds.width() + bounds.origin_x() - event.position.x(),
|
||||
// Handle on bottom side of element => Element is on the top
|
||||
HandleSide::Bottom => event.position.y() - bounds.lower_left().y(),
|
||||
};
|
||||
|
||||
let new_size = min_size.max(new_size_raw).min(max_size).round();
|
||||
if new_size != prev_size {
|
||||
on_resize.borrow_mut()(view, new_size, cx);
|
||||
on_resize.borrow_mut()(view, Some(new_size), cx);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
@ -201,3 +215,80 @@ impl<V: View> Element<V> for Resizable<V> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ProviderMap(HashMap<TypeTag, (RectF, RectF)>);
|
||||
|
||||
pub struct BoundsProvider<V: View, P> {
|
||||
child: AnyElement<V>,
|
||||
phantom: std::marker::PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<V: View, P: 'static> BoundsProvider<V, P> {
|
||||
pub fn new(child: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
child,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, P: 'static> Element<V> for BoundsProvider<V, P> {
|
||||
type LayoutState = ();
|
||||
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: crate::SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut crate::LayoutContext<V>,
|
||||
) -> (pathfinder_geometry::vector::Vector2F, Self::LayoutState) {
|
||||
(self.child.layout(constraint, view, cx), ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut crate::SceneBuilder,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
visible_bounds: pathfinder_geometry::rect::RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut crate::PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
cx.update_default_global::<ProviderMap, _, _>(|map, _| {
|
||||
map.0.insert(TypeTag::new::<P>(), (bounds, visible_bounds));
|
||||
});
|
||||
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
range_utf16: std::ops::Range<usize>,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &crate::ViewContext<V>,
|
||||
) -> Option<pathfinder_geometry::rect::RectF> {
|
||||
self.child.rect_for_text_range(range_utf16, view, cx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &crate::ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "Provider",
|
||||
"providing": format!("{:?}", TypeTag::new::<P>()),
|
||||
"child": self.child.debug(view, cx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ pub trait InputHandler {
|
|||
pub trait Screen: Debug {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn bounds(&self) -> RectF;
|
||||
fn content_bounds(&self) -> RectF;
|
||||
fn display_uuid(&self) -> Option<Uuid>;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,7 @@ use cocoa::{
|
|||
foundation::{NSPoint, NSRect},
|
||||
};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
};
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
|
||||
///! Macos screen have a y axis that goings up from the bottom of the screen and
|
||||
///! an origin at the bottom left of the main display.
|
||||
|
@ -15,6 +12,7 @@ pub trait Vector2FExt {
|
|||
/// Converts self to an NSPoint with y axis pointing up.
|
||||
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint;
|
||||
}
|
||||
|
||||
impl Vector2FExt for Vector2F {
|
||||
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint {
|
||||
unsafe {
|
||||
|
@ -25,16 +23,13 @@ impl Vector2FExt for Vector2F {
|
|||
}
|
||||
|
||||
pub trait NSRectExt {
|
||||
fn to_rectf(&self) -> RectF;
|
||||
fn size_vec(&self) -> Vector2F;
|
||||
fn intersects(&self, other: Self) -> bool;
|
||||
}
|
||||
|
||||
impl NSRectExt for NSRect {
|
||||
fn to_rectf(&self) -> RectF {
|
||||
RectF::new(
|
||||
vec2f(self.origin.x as f32, self.origin.y as f32),
|
||||
vec2f(self.size.width as f32, self.size.height as f32),
|
||||
)
|
||||
fn size_vec(&self) -> Vector2F {
|
||||
vec2f(self.size.width as f32, self.size.height as f32)
|
||||
}
|
||||
|
||||
fn intersects(&self, other: Self) -> bool {
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
use std::{any::Any, ffi::c_void};
|
||||
|
||||
use super::ns_string;
|
||||
use crate::platform;
|
||||
use cocoa::{
|
||||
appkit::NSScreen,
|
||||
base::{id, nil},
|
||||
foundation::{NSArray, NSDictionary},
|
||||
foundation::{NSArray, NSDictionary, NSPoint, NSRect, NSSize},
|
||||
};
|
||||
use core_foundation::{
|
||||
number::{kCFNumberIntType, CFNumberGetValue, CFNumberRef},
|
||||
uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
|
||||
};
|
||||
use core_graphics::display::CGDirectDisplayID;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use pathfinder_geometry::{rect::RectF, vector::vec2f};
|
||||
use std::{any::Any, ffi::c_void};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{geometry::NSRectExt, ns_string};
|
||||
|
||||
#[link(name = "ApplicationServices", kind = "framework")]
|
||||
extern "C" {
|
||||
pub fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
|
||||
|
@ -27,29 +25,58 @@ pub struct Screen {
|
|||
}
|
||||
|
||||
impl Screen {
|
||||
/// Get the screen with the given UUID.
|
||||
pub fn find_by_id(uuid: Uuid) -> Option<Self> {
|
||||
Self::all().find(|screen| platform::Screen::display_uuid(screen) == Some(uuid))
|
||||
}
|
||||
|
||||
/// Get the primary screen - the one with the menu bar, and whose bottom left
|
||||
/// corner is at the origin of the AppKit coordinate system.
|
||||
fn primary() -> Self {
|
||||
Self::all().next().unwrap()
|
||||
}
|
||||
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
unsafe {
|
||||
let native_screens = NSScreen::screens(nil);
|
||||
(0..NSArray::count(native_screens))
|
||||
.into_iter()
|
||||
.map(|ix| Screen {
|
||||
native_screen: native_screens.objectAtIndex(ix),
|
||||
})
|
||||
.find(|screen| platform::Screen::display_uuid(screen) == Some(uuid))
|
||||
(0..NSArray::count(native_screens)).map(move |ix| Screen {
|
||||
native_screen: native_screens.objectAtIndex(ix),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all() -> Vec<Self> {
|
||||
let mut screens = Vec::new();
|
||||
unsafe {
|
||||
let native_screens = NSScreen::screens(nil);
|
||||
for ix in 0..NSArray::count(native_screens) {
|
||||
screens.push(Screen {
|
||||
native_screen: native_screens.objectAtIndex(ix),
|
||||
});
|
||||
}
|
||||
}
|
||||
screens
|
||||
/// Convert the given rectangle in screen coordinates from GPUI's
|
||||
/// coordinate system to the AppKit coordinate system.
|
||||
///
|
||||
/// In GPUI's coordinates, the origin is at the top left of the primary screen, with
|
||||
/// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
|
||||
/// bottom left of the primary screen, with the Y axis pointing upward.
|
||||
pub(crate) fn screen_rect_to_native(rect: RectF) -> NSRect {
|
||||
let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
|
||||
NSRect::new(
|
||||
NSPoint::new(
|
||||
rect.origin_x() as f64,
|
||||
primary_screen_height - rect.origin_y() as f64 - rect.height() as f64,
|
||||
),
|
||||
NSSize::new(rect.width() as f64, rect.height() as f64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert the given rectangle in screen coordinates from the AppKit
|
||||
/// coordinate system to GPUI's coordinate system.
|
||||
///
|
||||
/// In GPUI's coordinates, the origin is at the top left of the primary screen, with
|
||||
/// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
|
||||
/// bottom left of the primary screen, with the Y axis pointing upward.
|
||||
pub(crate) fn screen_rect_from_native(rect: NSRect) -> RectF {
|
||||
let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
|
||||
RectF::new(
|
||||
vec2f(
|
||||
rect.origin.x as f32,
|
||||
(primary_screen_height - rect.origin.y - rect.size.height) as f32,
|
||||
),
|
||||
vec2f(rect.size.width as f32, rect.size.height as f32),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,9 +135,10 @@ impl platform::Screen for Screen {
|
|||
}
|
||||
|
||||
fn bounds(&self) -> RectF {
|
||||
unsafe {
|
||||
let frame = self.native_screen.frame();
|
||||
frame.to_rectf()
|
||||
}
|
||||
unsafe { Self::screen_rect_from_native(self.native_screen.frame()) }
|
||||
}
|
||||
|
||||
fn content_bounds(&self) -> RectF {
|
||||
unsafe { Self::screen_rect_from_native(self.native_screen.visibleFrame()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -368,32 +368,20 @@ impl WindowState {
|
|||
return WindowBounds::Fullscreen;
|
||||
}
|
||||
|
||||
let window_frame = self.frame();
|
||||
let screen_frame = self.native_window.screen().visibleFrame().to_rectf();
|
||||
if window_frame.size() == screen_frame.size() {
|
||||
let frame = self.frame();
|
||||
let screen_size = self.native_window.screen().visibleFrame().size_vec();
|
||||
if frame.size() == screen_size {
|
||||
WindowBounds::Maximized
|
||||
} else {
|
||||
WindowBounds::Fixed(window_frame)
|
||||
WindowBounds::Fixed(frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the window bounds in window coordinates
|
||||
fn frame(&self) -> RectF {
|
||||
unsafe {
|
||||
let screen_frame = self.native_window.screen().visibleFrame();
|
||||
let window_frame = NSWindow::frame(self.native_window);
|
||||
RectF::new(
|
||||
vec2f(
|
||||
window_frame.origin.x as f32,
|
||||
(screen_frame.size.height - window_frame.origin.y - window_frame.size.height)
|
||||
as f32,
|
||||
),
|
||||
vec2f(
|
||||
window_frame.size.width as f32,
|
||||
window_frame.size.height as f32,
|
||||
),
|
||||
)
|
||||
let frame = NSWindow::frame(self.native_window);
|
||||
Screen::screen_rect_from_native(frame)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,21 +468,12 @@ impl MacWindow {
|
|||
native_window.setFrame_display_(screen.visibleFrame(), YES);
|
||||
}
|
||||
WindowBounds::Fixed(rect) => {
|
||||
let screen_frame = screen.visibleFrame();
|
||||
let ns_rect = NSRect::new(
|
||||
NSPoint::new(
|
||||
rect.origin_x() as f64,
|
||||
screen_frame.size.height
|
||||
- rect.origin_y() as f64
|
||||
- rect.height() as f64,
|
||||
),
|
||||
NSSize::new(rect.width() as f64, rect.height() as f64),
|
||||
);
|
||||
|
||||
if ns_rect.intersects(screen_frame) {
|
||||
native_window.setFrame_display_(ns_rect, YES);
|
||||
let bounds = Screen::screen_rect_to_native(rect);
|
||||
let screen_bounds = screen.visibleFrame();
|
||||
if bounds.intersects(screen_bounds) {
|
||||
native_window.setFrame_display_(bounds, YES);
|
||||
} else {
|
||||
native_window.setFrame_display_(screen_frame, YES);
|
||||
native_window.setFrame_display_(screen_bounds, YES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,6 +250,10 @@ impl super::Screen for Screen {
|
|||
RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
|
||||
}
|
||||
|
||||
fn content_bounds(&self) -> RectF {
|
||||
RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
|
||||
}
|
||||
|
||||
fn display_uuid(&self) -> Option<uuid::Uuid> {
|
||||
Some(uuid::Uuid::new_v4())
|
||||
}
|
||||
|
|
|
@ -1651,8 +1651,8 @@ impl workspace::dock::Panel for ProjectPanel {
|
|||
.unwrap_or_else(|| settings::get::<ProjectPanelSettings>(cx).default_width)
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
|
||||
self.width = Some(size);
|
||||
fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
|
||||
self.width = size;
|
||||
self.serialize(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
|
|
@ -362,10 +362,10 @@ impl Panel for TerminalPanel {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
|
||||
fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
|
||||
match self.position(cx) {
|
||||
DockPosition::Left | DockPosition::Right => self.width = Some(size),
|
||||
DockPosition::Bottom => self.height = Some(size),
|
||||
DockPosition::Left | DockPosition::Right => self.width = size,
|
||||
DockPosition::Bottom => self.height = size,
|
||||
}
|
||||
self.serialize(cx);
|
||||
cx.notify();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{StatusItemView, Workspace};
|
||||
use crate::{StatusItemView, Workspace, WorkspaceBounds};
|
||||
use context_menu::{ContextMenu, ContextMenuItem};
|
||||
use gpui::{
|
||||
elements::*, platform::CursorStyle, platform::MouseButton, Action, AnyViewHandle, AppContext,
|
||||
|
@ -13,7 +13,7 @@ pub trait Panel: View {
|
|||
fn position_is_valid(&self, position: DockPosition) -> bool;
|
||||
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
|
||||
fn size(&self, cx: &WindowContext) -> f32;
|
||||
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>);
|
||||
fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>);
|
||||
fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
|
||||
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>);
|
||||
fn icon_label(&self, _: &WindowContext) -> Option<String> {
|
||||
|
@ -50,7 +50,7 @@ pub trait PanelHandle {
|
|||
fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
|
||||
fn set_active(&self, active: bool, cx: &mut WindowContext);
|
||||
fn size(&self, cx: &WindowContext) -> f32;
|
||||
fn set_size(&self, size: f32, cx: &mut WindowContext);
|
||||
fn set_size(&self, size: Option<f32>, cx: &mut WindowContext);
|
||||
fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
|
||||
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
|
||||
fn icon_label(&self, cx: &WindowContext) -> Option<String>;
|
||||
|
@ -82,7 +82,7 @@ where
|
|||
self.read(cx).size(cx)
|
||||
}
|
||||
|
||||
fn set_size(&self, size: f32, cx: &mut WindowContext) {
|
||||
fn set_size(&self, size: Option<f32>, cx: &mut WindowContext) {
|
||||
self.update(cx, |this, cx| this.set_size(size, cx))
|
||||
}
|
||||
|
||||
|
@ -373,7 +373,7 @@ impl Dock {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resize_active_panel(&mut self, size: f32, cx: &mut ViewContext<Self>) {
|
||||
pub fn resize_active_panel(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
|
||||
if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) {
|
||||
entry.panel.set_size(size, cx);
|
||||
cx.notify();
|
||||
|
@ -386,7 +386,7 @@ impl Dock {
|
|||
.into_any()
|
||||
.contained()
|
||||
.with_style(self.style(cx))
|
||||
.resizable(
|
||||
.resizable::<WorkspaceBounds>(
|
||||
self.position.to_resize_handle_side(),
|
||||
active_entry.panel.size(cx),
|
||||
|_, _, _| {},
|
||||
|
@ -423,7 +423,7 @@ impl View for Dock {
|
|||
ChildView::new(active_entry.panel.as_any(), cx)
|
||||
.contained()
|
||||
.with_style(style)
|
||||
.resizable(
|
||||
.resizable::<WorkspaceBounds>(
|
||||
self.position.to_resize_handle_side(),
|
||||
active_entry.panel.size(cx),
|
||||
|dock: &mut Self, size, cx| dock.resize_active_panel(size, cx),
|
||||
|
@ -700,8 +700,8 @@ pub mod test {
|
|||
self.size
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: f32, _: &mut ViewContext<Self>) {
|
||||
self.size = size;
|
||||
fn set_size(&mut self, size: Option<f32>, _: &mut ViewContext<Self>) {
|
||||
self.size = size.unwrap_or(300.);
|
||||
}
|
||||
|
||||
fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
|
||||
|
|
|
@ -553,6 +553,8 @@ struct FollowerState {
|
|||
items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
|
||||
}
|
||||
|
||||
enum WorkspaceBounds {}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(
|
||||
workspace_id: WorkspaceId,
|
||||
|
@ -3776,6 +3778,7 @@ impl View for Workspace {
|
|||
}))
|
||||
.with_children(self.render_notifications(&theme.workspace, cx)),
|
||||
))
|
||||
.provide_resize_bounds::<WorkspaceBounds>()
|
||||
.flex(1.0, true),
|
||||
)
|
||||
.with_child(ChildView::new(&self.status_bar, cx))
|
||||
|
@ -4859,7 +4862,9 @@ mod tests {
|
|||
panel_1.size(cx)
|
||||
);
|
||||
|
||||
left_dock.update(cx, |left_dock, cx| left_dock.resize_active_panel(1337., cx));
|
||||
left_dock.update(cx, |left_dock, cx| {
|
||||
left_dock.resize_active_panel(Some(1337.), cx)
|
||||
});
|
||||
assert_eq!(
|
||||
workspace
|
||||
.right_dock()
|
||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
|||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.100.0"
|
||||
version = "0.100.1"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1 +1 @@
|
|||
dev
|
||||
stable
|
|
@ -12,7 +12,7 @@ if [[ -n $(git status --short --untracked-files=no) ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
which cargo-set-version > /dev/null || cargo install cargo-edit
|
||||
which cargo-set-version > /dev/null || cargo install cargo-edit --features vendored-openssl
|
||||
which jq > /dev/null || brew install jq
|
||||
cargo set-version --package $package --bump $version_increment
|
||||
cargo check --quiet
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { background } from "../style_tree/components"
|
||||
import { foreground } from "../style_tree/components"
|
||||
import { Layer, StyleSets } from "../theme"
|
||||
|
||||
export const indicator = ({ layer, color }: { layer: Layer, color: StyleSets }) => ({
|
||||
corner_radius: 4,
|
||||
padding: 4,
|
||||
margin: { top: 12, left: 12 },
|
||||
background: background(layer, color),
|
||||
background: foreground(layer, color),
|
||||
})
|
||||
|
|
|
@ -76,7 +76,8 @@ export default function channel_modal(): any {
|
|||
},
|
||||
|
||||
},
|
||||
max_height: 400,
|
||||
// FIXME: due to a bug in the picker's size calculation, this must be 600
|
||||
max_height: 600,
|
||||
max_width: 540,
|
||||
title: {
|
||||
...text(theme.middle, "sans", "on", { size: "lg" }),
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
import { interactive, toggleable } from "../element"
|
||||
import { useTheme } from "../theme"
|
||||
import collab_modals from "./collab_modals"
|
||||
import { text_button } from "../component/text_button"
|
||||
import { icon_button, toggleable_icon_button } from "../component/icon_button"
|
||||
import { indicator } from "../component/indicator"
|
||||
|
||||
|
@ -38,7 +37,7 @@ export default function contacts_panel(): any {
|
|||
width: 14,
|
||||
},
|
||||
name: {
|
||||
...text(layer, "ui_sans", { size: "sm" }),
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
margin: {
|
||||
left: NAME_MARGIN,
|
||||
right: 4,
|
||||
|
@ -70,7 +69,7 @@ export default function contacts_panel(): any {
|
|||
const subheader_row = toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
...text(layer, "ui_sans", { size: "sm" }),
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
padding: {
|
||||
left: SPACING,
|
||||
right: SPACING,
|
||||
|
@ -88,7 +87,7 @@ export default function contacts_panel(): any {
|
|||
state: {
|
||||
active: {
|
||||
default: {
|
||||
...text(theme.lowest, "ui_sans", { size: "sm" }),
|
||||
...text(theme.lowest, "sans", { size: "sm" }),
|
||||
background: background(theme.lowest),
|
||||
},
|
||||
clicked: {
|
||||
|
@ -101,8 +100,8 @@ export default function contacts_panel(): any {
|
|||
const filter_input = {
|
||||
background: background(layer, "on"),
|
||||
corner_radius: 6,
|
||||
text: text(layer, "ui_sans", "base"),
|
||||
placeholder_text: text(layer, "ui_sans", "base", "disabled", {
|
||||
text: text(layer, "sans", "base"),
|
||||
placeholder_text: text(layer, "sans", "base", "disabled", {
|
||||
size: "xs",
|
||||
}),
|
||||
selection: theme.players[0],
|
||||
|
@ -141,7 +140,7 @@ export default function contacts_panel(): any {
|
|||
},
|
||||
active: {
|
||||
default: {
|
||||
...text(theme.lowest, "ui_sans", { size: "sm" }),
|
||||
...text(theme.lowest, "sans", { size: "sm" }),
|
||||
background: background(theme.lowest),
|
||||
},
|
||||
clicked: {
|
||||
|
@ -195,10 +194,10 @@ export default function contacts_panel(): any {
|
|||
add_channel_button: header_icon_button,
|
||||
leave_call_button: header_icon_button,
|
||||
row_height: ITEM_HEIGHT,
|
||||
channel_indent: INDENT_SIZE,
|
||||
channel_indent: INDENT_SIZE * 2,
|
||||
section_icon_size: 14,
|
||||
header_row: {
|
||||
...text(layer, "ui_sans", { size: "sm", weight: "bold" }),
|
||||
...text(layer, "sans", { size: "sm", weight: "bold" }),
|
||||
margin: { top: SPACING },
|
||||
padding: {
|
||||
left: SPACING,
|
||||
|
@ -252,7 +251,7 @@ export default function contacts_panel(): any {
|
|||
},
|
||||
active: {
|
||||
default: {
|
||||
...text(theme.lowest, "ui_sans", { size: "sm" }),
|
||||
...text(theme.lowest, "sans", { size: "sm" }),
|
||||
background: background(theme.lowest),
|
||||
},
|
||||
clicked: {
|
||||
|
@ -263,7 +262,7 @@ export default function contacts_panel(): any {
|
|||
}),
|
||||
channel_row: item_row,
|
||||
channel_name: {
|
||||
...text(layer, "ui_sans", { size: "sm" }),
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
margin: {
|
||||
left: NAME_MARGIN,
|
||||
},
|
||||
|
@ -280,7 +279,7 @@ export default function contacts_panel(): any {
|
|||
list_empty_state: toggleable({
|
||||
base: interactive({
|
||||
base: {
|
||||
...text(layer, "ui_sans", "variant", { size: "sm" }),
|
||||
...text(layer, "sans", "variant", { size: "sm" }),
|
||||
padding: {
|
||||
top: SPACING / 2,
|
||||
bottom: SPACING / 2,
|
||||
|
@ -302,7 +301,7 @@ export default function contacts_panel(): any {
|
|||
},
|
||||
active: {
|
||||
default: {
|
||||
...text(theme.lowest, "ui_sans", { size: "sm" }),
|
||||
...text(theme.lowest, "sans", { size: "sm" }),
|
||||
background: background(theme.lowest),
|
||||
},
|
||||
clicked: {
|
||||
|
@ -326,12 +325,12 @@ export default function contacts_panel(): any {
|
|||
right: 4,
|
||||
},
|
||||
background: background(layer, "hovered"),
|
||||
...text(layer, "ui_sans", "hovered", { size: "xs" })
|
||||
...text(layer, "sans", "hovered", { size: "xs" })
|
||||
},
|
||||
contact_status_free: indicator({ layer, color: "positive" }),
|
||||
contact_status_busy: indicator({ layer, color: "negative" }),
|
||||
contact_username: {
|
||||
...text(layer, "ui_sans", { size: "sm" }),
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
margin: {
|
||||
left: NAME_MARGIN,
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@ export default function context_menu(): any {
|
|||
icon_width: 14,
|
||||
padding: { left: 6, right: 6, top: 2, bottom: 2 },
|
||||
corner_radius: 6,
|
||||
label: text(theme.middle, "ui_sans", { size: "sm" }),
|
||||
label: text(theme.middle, "sans", { size: "sm" }),
|
||||
keystroke: {
|
||||
...text(theme.middle, "sans", "variant", {
|
||||
size: "sm",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue