Show borders around avatars and panes to indicate following state
This commit is contained in:
parent
06cd9ac664
commit
c8f36af823
4 changed files with 82 additions and 19 deletions
|
@ -35,6 +35,7 @@ pub struct Workspace {
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
pub active_tab: Tab,
|
pub active_tab: Tab,
|
||||||
pub pane_divider: Border,
|
pub pane_divider: Border,
|
||||||
|
pub leader_border_opacity: f32,
|
||||||
pub left_sidebar: Sidebar,
|
pub left_sidebar: Sidebar,
|
||||||
pub right_sidebar: Sidebar,
|
pub right_sidebar: Sidebar,
|
||||||
pub status_bar: StatusBar,
|
pub status_bar: StatusBar,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
use crate::{FollowerStatesByLeader, Pane};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use gpui::{elements::*, Axis, ViewHandle};
|
use client::PeerId;
|
||||||
|
use collections::HashMap;
|
||||||
|
use gpui::{elements::*, Axis, Border, ViewHandle};
|
||||||
|
use project::Collaborator;
|
||||||
use theme::Theme;
|
use theme::Theme;
|
||||||
|
|
||||||
use crate::Pane;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct PaneGroup {
|
pub struct PaneGroup {
|
||||||
root: Member,
|
root: Member,
|
||||||
|
@ -47,8 +49,13 @@ impl PaneGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<'a>(&self, theme: &Theme) -> ElementBox {
|
pub(crate) fn render<'a>(
|
||||||
self.root.render(theme)
|
&self,
|
||||||
|
theme: &Theme,
|
||||||
|
follower_states: &FollowerStatesByLeader,
|
||||||
|
collaborators: &HashMap<PeerId, Collaborator>,
|
||||||
|
) -> ElementBox {
|
||||||
|
self.root.render(theme, follower_states, collaborators)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,10 +87,39 @@ impl Member {
|
||||||
Member::Axis(PaneAxis { axis, members })
|
Member::Axis(PaneAxis { axis, members })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, theme: &Theme) -> ElementBox {
|
pub fn render(
|
||||||
|
&self,
|
||||||
|
theme: &Theme,
|
||||||
|
follower_states: &FollowerStatesByLeader,
|
||||||
|
collaborators: &HashMap<PeerId, Collaborator>,
|
||||||
|
) -> ElementBox {
|
||||||
match self {
|
match self {
|
||||||
Member::Pane(pane) => ChildView::new(pane).boxed(),
|
Member::Pane(pane) => {
|
||||||
Member::Axis(axis) => axis.render(theme),
|
let mut border = Border::default();
|
||||||
|
let leader = follower_states
|
||||||
|
.iter()
|
||||||
|
.find_map(|(leader_id, follower_states)| {
|
||||||
|
if follower_states.contains_key(pane) {
|
||||||
|
Some(leader_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.and_then(|leader_id| collaborators.get(leader_id));
|
||||||
|
if let Some(leader) = leader {
|
||||||
|
let leader_color = theme
|
||||||
|
.editor
|
||||||
|
.replica_selection_style(leader.replica_id)
|
||||||
|
.cursor;
|
||||||
|
border = Border::all(1.0, leader_color);
|
||||||
|
border
|
||||||
|
.color
|
||||||
|
.fade_out(1. - theme.workspace.leader_border_opacity);
|
||||||
|
border.overlay = true;
|
||||||
|
}
|
||||||
|
ChildView::new(pane).contained().with_border(border).boxed()
|
||||||
|
}
|
||||||
|
Member::Axis(axis) => axis.render(theme, follower_states, collaborators),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,11 +208,16 @@ impl PaneAxis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<'a>(&self, theme: &Theme) -> ElementBox {
|
fn render(
|
||||||
|
&self,
|
||||||
|
theme: &Theme,
|
||||||
|
follower_state: &FollowerStatesByLeader,
|
||||||
|
collaborators: &HashMap<PeerId, Collaborator>,
|
||||||
|
) -> ElementBox {
|
||||||
let last_member_ix = self.members.len() - 1;
|
let last_member_ix = self.members.len() - 1;
|
||||||
Flex::new(self.axis)
|
Flex::new(self.axis)
|
||||||
.with_children(self.members.iter().enumerate().map(|(ix, member)| {
|
.with_children(self.members.iter().enumerate().map(|(ix, member)| {
|
||||||
let mut member = member.render(theme);
|
let mut member = member.render(theme, follower_state, collaborators);
|
||||||
if ix < last_member_ix {
|
if ix < last_member_ix {
|
||||||
let mut border = theme.workspace.pane_divider;
|
let mut border = theme.workspace.pane_divider;
|
||||||
border.left = false;
|
border.left = false;
|
||||||
|
|
|
@ -20,9 +20,9 @@ use gpui::{
|
||||||
json::{self, to_string_pretty, ToJson},
|
json::{self, to_string_pretty, ToJson},
|
||||||
keymap::Binding,
|
keymap::Binding,
|
||||||
platform::{CursorStyle, WindowOptions},
|
platform::{CursorStyle, WindowOptions},
|
||||||
AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Entity, ImageData,
|
AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Border, ClipboardItem, Entity,
|
||||||
ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View,
|
ImageData, ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task,
|
||||||
ViewContext, ViewHandle, WeakViewHandle,
|
View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
@ -613,7 +613,7 @@ pub struct Workspace {
|
||||||
status_bar: ViewHandle<StatusBar>,
|
status_bar: ViewHandle<StatusBar>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
leader_state: LeaderState,
|
leader_state: LeaderState,
|
||||||
follower_states_by_leader: HashMap<PeerId, HashMap<ViewHandle<Pane>, FollowerState>>,
|
follower_states_by_leader: FollowerStatesByLeader,
|
||||||
_observe_current_user: Task<()>,
|
_observe_current_user: Task<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,6 +622,8 @@ struct LeaderState {
|
||||||
followers: HashSet<PeerId>,
|
followers: HashSet<PeerId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FollowerStatesByLeader = HashMap<PeerId, HashMap<ViewHandle<Pane>, FollowerState>>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct FollowerState {
|
struct FollowerState {
|
||||||
active_view_id: Option<u64>,
|
active_view_id: Option<u64>,
|
||||||
|
@ -1262,6 +1264,7 @@ impl Workspace {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<PeerId> {
|
) -> Option<PeerId> {
|
||||||
for (leader_id, states_by_pane) in &mut self.follower_states_by_leader {
|
for (leader_id, states_by_pane) in &mut self.follower_states_by_leader {
|
||||||
|
let leader_id = *leader_id;
|
||||||
if let Some(state) = states_by_pane.remove(&pane) {
|
if let Some(state) = states_by_pane.remove(&pane) {
|
||||||
for (_, item) in state.items_by_leader_view_id {
|
for (_, item) in state.items_by_leader_view_id {
|
||||||
if let FollowerItem::Loaded(item) = item {
|
if let FollowerItem::Loaded(item) = item {
|
||||||
|
@ -1270,6 +1273,7 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
if states_by_pane.is_empty() {
|
if states_by_pane.is_empty() {
|
||||||
|
self.follower_states_by_leader.remove(&leader_id);
|
||||||
if let Some(project_id) = self.project.read(cx).remote_id() {
|
if let Some(project_id) = self.project.read(cx).remote_id() {
|
||||||
self.client
|
self.client
|
||||||
.send(proto::Unfollow {
|
.send(proto::Unfollow {
|
||||||
|
@ -1281,7 +1285,7 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return Some(*leader_id);
|
return Some(leader_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -1420,17 +1424,25 @@ impl Workspace {
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
cx: &mut RenderContext<Self>,
|
cx: &mut RenderContext<Self>,
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
|
let replica_color = theme.editor.replica_selection_style(replica_id).cursor;
|
||||||
|
let is_followed = peer_id.map_or(false, |peer_id| {
|
||||||
|
self.follower_states_by_leader.contains_key(&peer_id)
|
||||||
|
});
|
||||||
|
let mut avatar_style = theme.workspace.titlebar.avatar;
|
||||||
|
if is_followed {
|
||||||
|
avatar_style.border = Border::all(1.0, replica_color);
|
||||||
|
}
|
||||||
let content = Stack::new()
|
let content = Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
Image::new(avatar)
|
Image::new(avatar)
|
||||||
.with_style(theme.workspace.titlebar.avatar)
|
.with_style(avatar_style)
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_width(theme.workspace.titlebar.avatar_width)
|
.with_width(theme.workspace.titlebar.avatar_width)
|
||||||
.aligned()
|
.aligned()
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
AvatarRibbon::new(theme.editor.replica_selection_style(replica_id).cursor)
|
AvatarRibbon::new(replica_color)
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_width(theme.workspace.titlebar.avatar_ribbon.width)
|
.with_width(theme.workspace.titlebar.avatar_ribbon.width)
|
||||||
.with_height(theme.workspace.titlebar.avatar_ribbon.height)
|
.with_height(theme.workspace.titlebar.avatar_ribbon.height)
|
||||||
|
@ -1800,8 +1812,16 @@ impl View for Workspace {
|
||||||
content.add_child(
|
content.add_child(
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.with_child(
|
.with_child(
|
||||||
Flexible::new(1., true, self.center.render(&theme))
|
Flexible::new(
|
||||||
.boxed(),
|
1.,
|
||||||
|
true,
|
||||||
|
self.center.render(
|
||||||
|
&theme,
|
||||||
|
&self.follower_states_by_leader,
|
||||||
|
self.project.read(cx).collaborators(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(ChildView::new(&self.status_bar).boxed())
|
.with_child(ChildView::new(&self.status_bar).boxed())
|
||||||
.flexible(1., true)
|
.flexible(1., true)
|
||||||
|
|
|
@ -4,6 +4,7 @@ base = { family = "Zed Sans", size = 14 }
|
||||||
[workspace]
|
[workspace]
|
||||||
background = "$surface.0"
|
background = "$surface.0"
|
||||||
pane_divider = { width = 1, color = "$border.0" }
|
pane_divider = { width = 1, color = "$border.0" }
|
||||||
|
leader_border_opacity = 0.6
|
||||||
|
|
||||||
[workspace.titlebar]
|
[workspace.titlebar]
|
||||||
height = 32
|
height = 32
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue