Render an offline icon in titlebar when connection is lost

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo
2021-09-14 17:47:43 -06:00
co-authored by Max Brunsfeld
parent e212461dfe
commit 99a2dc4880
11 changed files with 102 additions and 40 deletions
+6 -6
View File
@@ -13,7 +13,7 @@ use crate::{
Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize)]
pub struct ContainerStyle {
#[serde(default)]
pub margin: Margin,
@@ -42,8 +42,8 @@ impl Container {
}
}
pub fn with_style(mut self, style: &ContainerStyle) -> Self {
self.style = style.clone();
pub fn with_style(mut self, style: ContainerStyle) -> Self {
self.style = style;
self
}
@@ -242,7 +242,7 @@ impl ToJson for ContainerStyle {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Margin {
pub top: f32,
pub left: f32,
@@ -269,7 +269,7 @@ impl ToJson for Margin {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Padding {
pub top: f32,
pub left: f32,
@@ -367,7 +367,7 @@ impl ToJson for Padding {
}
}
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize)]
pub struct Shadow {
#[serde(default, deserialize_with = "deserialize_vec2f")]
offset: Vector2F,
+2 -2
View File
@@ -111,7 +111,7 @@ impl View for Select {
mouse_state.hovered,
cx,
))
.with_style(&style.header)
.with_style(style.header)
.boxed()
})
.on_click(move |cx| cx.dispatch_action(ToggleSelect))
@@ -158,7 +158,7 @@ impl View for Select {
.with_max_height(200.)
.boxed(),
)
.with_style(&style.menu)
.with_style(style.menu)
.boxed(),
)
.boxed(),
+3
View File
@@ -0,0 +1,3 @@
<svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.14992 9.84972C1.99189 9.84972 1.04997 8.90759 1.04997 7.74977C1.04997 6.87042 1.61368 6.07944 2.45278 5.78173L3.15692 5.53193L3.15307 4.98616L2.15704 4.18643C2.12729 4.39861 2.10016 4.59111 2.1017 4.79235C0.880227 5.22546 0 6.38043 0 7.74977C0 9.48879 1.41024 10.8997 3.14992 10.8997H10.6988L9.3592 9.84972H3.14992ZM13.0569 10.0816C13.6343 9.5391 13.9996 8.77787 13.9996 7.92477C13.9996 6.58321 13.1056 5.46171 11.8855 5.09203C11.8888 5.04391 11.8997 4.99797 11.8997 4.94985C11.8997 3.59582 10.8038 2.49991 9.44976 2.49991C9.20017 2.49991 8.96436 2.54819 8.73752 2.61753C8.06948 1.70171 6.99544 1.09995 5.77485 1.09995C4.71394 1.09995 3.76678 1.55668 3.1018 2.27679L0.849166 0.51172C0.752699 0.436537 0.638515 0.399963 0.525643 0.399963C0.369897 0.399963 0.215398 0.468999 0.112194 0.600946C-0.0669139 0.82914 -0.0272556 1.15944 0.201048 1.33816L13.1509 11.4881C13.3806 11.6676 13.71 11.6265 13.8879 11.3989C14.067 11.1705 14.0273 10.8405 13.799 10.6617L13.0569 10.0816ZM12.2169 9.42317L3.92865 2.92427C4.40114 2.44916 5.05081 2.14992 5.77485 2.14992C6.61483 2.14992 7.38547 2.54585 7.88923 3.23642L8.32979 3.84016L9.04442 3.62167C10.1132 3.29487 10.8869 4.18078 10.8373 5.03039L10.7893 5.85549L11.58 6.09589C12.3984 6.34544 12.9497 7.08042 12.9497 7.92477C12.9497 8.53288 12.6609 9.0688 12.2169 9.42317Z" fill="#B3B3B3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+6 -2
View File
@@ -9,9 +9,13 @@ pane_divider = { width = 1, color = "$border.0" }
border = { width = 1, bottom = true, color = "$border.0" }
title = "$text.0"
avatar_width = 20
icon_signed_out = "$text.2.color"
icon_color = "$text.2.color"
avatar = { corner_radius = 10, border = { width = 1, color = "#00000088" } }
[workspace.titlebar.offline_icon]
padding = { right = 4 }
width = 16
[workspace.tab]
text = "$text.2"
padding = { left = 10, right = 10 }
@@ -29,7 +33,7 @@ background = "$surface.1"
text = "$text.0"
[workspace.sidebar]
width = 38
width = 32
border = { right = true, width = 1, color = "$border.0" }
[workspace.sidebar.resize_handle]
+8 -8
View File
@@ -209,7 +209,7 @@ impl ChatPanel {
Flex::column()
.with_child(
Container::new(ChildView::new(self.channel_select.id()).boxed())
.with_style(&theme.chat_panel.channel_select.container)
.with_style(theme.chat_panel.channel_select.container)
.boxed(),
)
.with_child(self.render_active_channel_messages())
@@ -243,7 +243,7 @@ impl ChatPanel {
)
.boxed(),
)
.with_style(&theme.sender.container)
.with_style(theme.sender.container)
.boxed(),
)
.with_child(
@@ -254,7 +254,7 @@ impl ChatPanel {
)
.boxed(),
)
.with_style(&theme.timestamp.container)
.with_style(theme.timestamp.container)
.boxed(),
)
.boxed(),
@@ -262,14 +262,14 @@ impl ChatPanel {
.with_child(Text::new(message.body.clone(), theme.body.clone()).boxed())
.boxed(),
)
.with_style(&theme.container)
.with_style(theme.container)
.boxed()
}
fn render_input_box(&self) -> ElementBox {
let theme = &self.settings.borrow().theme;
Container::new(ChildView::new(self.input_editor.id()).boxed())
.with_style(&theme.chat_panel.input_editor.container)
.with_style(theme.chat_panel.input_editor.container)
.boxed()
}
@@ -293,13 +293,13 @@ impl ChatPanel {
Flex::row()
.with_child(
Container::new(Label::new("#".to_string(), theme.hash.text.clone()).boxed())
.with_style(&theme.hash.container)
.with_style(theme.hash.container)
.boxed(),
)
.with_child(Label::new(channel.name.clone(), theme.name.clone()).boxed())
.boxed(),
)
.with_style(&theme.container)
.with_style(theme.container)
.boxed()
}
@@ -387,7 +387,7 @@ impl View for ChatPanel {
};
ConstrainedBox::new(
Container::new(element)
.with_style(&theme.chat_panel.container)
.with_style(theme.chat_panel.container)
.boxed(),
)
.with_min_width(150.)
+4 -4
View File
@@ -88,13 +88,13 @@ impl View for FileFinder {
Flex::new(Axis::Vertical)
.with_child(
Container::new(ChildView::new(self.query_editor.id()).boxed())
.with_style(&settings.theme.selector.input_editor.container)
.with_style(settings.theme.selector.input_editor.container)
.boxed(),
)
.with_child(Flexible::new(1.0, self.render_matches()).boxed())
.boxed(),
)
.with_style(&settings.theme.selector.container)
.with_style(settings.theme.selector.container)
.boxed(),
)
.with_max_width(500.0)
@@ -127,7 +127,7 @@ impl FileFinder {
)
.boxed(),
)
.with_style(&settings.theme.selector.empty.container)
.with_style(settings.theme.selector.empty.container)
.named("empty matches");
}
@@ -200,7 +200,7 @@ impl FileFinder {
)
.boxed(),
)
.with_style(&style.container);
.with_style(style.container);
let action = Select(Entry {
worktree_id: path_match.tree_id,
+9 -1
View File
@@ -48,10 +48,18 @@ pub struct Titlebar {
pub container: ContainerStyle,
pub title: TextStyle,
pub avatar_width: f32,
pub icon_signed_out: Color,
pub offline_icon: OfflineIcon,
pub icon_color: Color,
pub avatar: ImageStyle,
}
#[derive(Clone, Deserialize)]
pub struct OfflineIcon {
#[serde(flatten)]
pub container: ContainerStyle,
pub width: f32,
}
#[derive(Clone, Deserialize)]
pub struct Tab {
#[serde(flatten)]
+4 -4
View File
@@ -214,7 +214,7 @@ impl ThemeSelector {
)
.boxed(),
)
.with_style(&settings.theme.selector.empty.container)
.with_style(settings.theme.selector.empty.container)
.named("empty matches");
}
@@ -259,9 +259,9 @@ impl ThemeSelector {
.boxed(),
)
.with_style(if index == self.selected_index {
&theme.selector.active_item.container
theme.selector.active_item.container
} else {
&theme.selector.item.container
theme.selector.item.container
});
container.boxed()
@@ -288,7 +288,7 @@ impl View for ThemeSelector {
.with_child(Flexible::new(1.0, self.render_matches(cx)).boxed())
.boxed(),
)
.with_style(&settings.theme.selector.container)
.with_style(settings.theme.selector.container)
.boxed(),
)
.with_max_width(600.0)
+45 -7
View File
@@ -31,7 +31,6 @@ pub use pane::*;
pub use pane_group::*;
use postage::{prelude::Stream, watch};
use sidebar::{Side, Sidebar, ToggleSidebarItem};
use smol::prelude::*;
use std::{
collections::{hash_map::Entry, HashMap, HashSet},
future::Future,
@@ -391,9 +390,14 @@ impl Workspace {
right_sidebar.add_item("icons/user-16.svg", cx.add_view(|_| ProjectBrowser).into());
let mut current_user = app_state.user_store.current_user().clone();
let mut connection_status = app_state.rpc.status().clone();
let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
current_user.recv().await;
while current_user.recv().await.is_some() {
connection_status.recv().await;
let mut stream =
Stream::map(current_user, drop).merge(Stream::map(connection_status, drop));
while stream.recv().await.is_some() {
cx.update(|cx| {
if let Some(this) = this.upgrade(&cx) {
this.update(cx, |_, cx| cx.notify());
@@ -642,7 +646,7 @@ impl Workspace {
if let Some(load_result) = watch.borrow().as_ref() {
break load_result.clone();
}
watch.next().await;
watch.recv().await;
};
this.update(&mut cx, |this, cx| {
@@ -954,7 +958,34 @@ impl Workspace {
&self.active_pane
}
fn render_current_user(&self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render_connection_status(&self) -> Option<ElementBox> {
let theme = &self.settings.borrow().theme;
match dbg!(&*self.rpc.status().borrow()) {
rpc::Status::ConnectionError
| rpc::Status::ConnectionLost
| rpc::Status::Reauthenticating
| rpc::Status::Reconnecting { .. }
| rpc::Status::ReconnectionError { .. } => Some(
Container::new(
Align::new(
ConstrainedBox::new(
Svg::new("icons/offline-14.svg")
.with_color(theme.workspace.titlebar.icon_color)
.boxed(),
)
.with_width(theme.workspace.titlebar.offline_icon.width)
.boxed(),
)
.boxed(),
)
.with_style(theme.workspace.titlebar.offline_icon.container)
.boxed(),
),
_ => None,
}
}
fn render_avatar(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = &self.settings.borrow().theme;
let avatar = if let Some(avatar) = self
.user_store
@@ -969,7 +1000,7 @@ impl Workspace {
} else {
MouseEventHandler::new::<Authenticate, _, _, _>(0, cx, |_, _| {
Svg::new("icons/signed-out-12.svg")
.with_color(theme.workspace.titlebar.icon_signed_out)
.with_color(theme.workspace.titlebar.icon_color)
.boxed()
})
.on_click(|cx| cx.dispatch_action(Authenticate))
@@ -1019,11 +1050,18 @@ impl View for Workspace {
.boxed(),
)
.with_child(
Align::new(self.render_current_user(cx)).right().boxed(),
Align::new(
Flex::row()
.with_children(self.render_connection_status())
.with_child(self.render_avatar(cx))
.boxed(),
)
.right()
.boxed(),
)
.boxed(),
)
.with_style(&theme.workspace.titlebar.container)
.with_style(theme.workspace.titlebar.container)
.boxed(),
)
.with_height(32.)
+13 -4
View File
@@ -1,6 +1,14 @@
use super::{ItemViewHandle, SplitDirection};
use crate::settings::Settings;
use gpui::{Border, Entity, MutableAppContext, Quad, RenderContext, View, ViewContext, ViewHandle, action, color::Color, elements::*, geometry::{rect::RectF, vector::vec2f}, keymap::Binding, platform::CursorStyle};
use gpui::{
action,
color::Color,
elements::*,
geometry::{rect::RectF, vector::vec2f},
keymap::Binding,
platform::CursorStyle,
Border, Entity, MutableAppContext, Quad, RenderContext, View, ViewContext, ViewHandle,
};
use postage::watch;
use std::{cmp, path::Path, sync::Arc};
@@ -256,7 +264,7 @@ impl Pane {
)
.boxed(),
)
.with_style(&ContainerStyle {
.with_style(ContainerStyle {
margin: Margin {
left: style.spacing,
right: style.spacing,
@@ -283,7 +291,8 @@ impl Pane {
icon.with_color(style.icon_close).boxed()
}
},
).with_cursor_style(CursorStyle::PointingHand)
)
.with_cursor_style(CursorStyle::PointingHand)
.on_click(move |cx| {
cx.dispatch_action(CloseItem(item_id))
})
@@ -298,7 +307,7 @@ impl Pane {
)
.boxed(),
)
.with_style(&style.container)
.with_style(style.container)
.boxed(),
)
.on_mouse_down(move |cx| {
+2 -2
View File
@@ -113,7 +113,7 @@ impl Sidebar {
}))
.boxed(),
)
.with_style(&theme.container)
.with_style(theme.container)
.boxed(),
)
.with_width(theme.width)
@@ -165,7 +165,7 @@ impl Sidebar {
let side = self.side;
MouseEventHandler::new::<Self, _, _, _>(self.side.id(), &mut cx, |_, _| {
Container::new(Empty::new().boxed())
.with_style(&self.theme(settings).resize_handle)
.with_style(self.theme(settings).resize_handle)
.boxed()
})
.with_padding(Padding {