Get the channel select looking good
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
parent
ff01b52819
commit
73a8fda9c7
+90
-40
@@ -1,12 +1,17 @@
|
||||
use crate::{
|
||||
channel::{Channel, ChannelEvent, ChannelList, ChannelMessage},
|
||||
editor::Editor,
|
||||
theme,
|
||||
util::ResultExt,
|
||||
Settings,
|
||||
};
|
||||
use gpui::{
|
||||
action, elements::*, keymap::Binding, Entity, ModelHandle, MutableAppContext, RenderContext,
|
||||
Subscription, View, ViewContext, ViewHandle,
|
||||
action,
|
||||
elements::*,
|
||||
keymap::Binding,
|
||||
views::{ItemType, Select, SelectStyle},
|
||||
AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription, View,
|
||||
ViewContext, ViewHandle,
|
||||
};
|
||||
use postage::watch;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
@@ -16,6 +21,7 @@ pub struct ChatPanel {
|
||||
active_channel: Option<(ModelHandle<Channel>, Subscription)>,
|
||||
message_list: ListState,
|
||||
input_editor: ViewHandle<Editor>,
|
||||
channel_select: ViewHandle<Select>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
}
|
||||
|
||||
@@ -38,11 +44,33 @@ impl ChatPanel {
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let input_editor = cx.add_view(|cx| Editor::auto_height(settings.clone(), cx));
|
||||
let channel_select = cx.add_view(|cx| {
|
||||
let channel_list = channel_list.clone();
|
||||
let theme = &settings.borrow().theme.chat_panel.channel_select;
|
||||
Select::new(0, cx, {
|
||||
let settings = settings.clone();
|
||||
move |ix, item_type, is_hovered, cx| {
|
||||
Self::render_channel_name(
|
||||
&channel_list,
|
||||
ix,
|
||||
item_type,
|
||||
is_hovered,
|
||||
&settings.borrow().theme.chat_panel.channel_select,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.with_style(&SelectStyle {
|
||||
header: theme.header.container.clone(),
|
||||
menu: theme.menu.clone(),
|
||||
})
|
||||
});
|
||||
let mut this = Self {
|
||||
channel_list,
|
||||
active_channel: Default::default(),
|
||||
message_list: ListState::new(0, Orientation::Bottom),
|
||||
input_editor,
|
||||
channel_select,
|
||||
settings,
|
||||
};
|
||||
|
||||
@@ -56,23 +84,33 @@ impl ChatPanel {
|
||||
}
|
||||
|
||||
fn init_active_channel(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if self.active_channel.is_none() {
|
||||
let channel = self.channel_list.update(cx, |list, cx| {
|
||||
if let Some(channel_id) = list
|
||||
.available_channels()
|
||||
.and_then(|channels| channels.first())
|
||||
.map(|details| details.id)
|
||||
{
|
||||
return list.get_channel(channel_id, cx);
|
||||
let (active_channel, channel_count) = self.channel_list.update(cx, |list, cx| {
|
||||
let channel_count;
|
||||
let mut active_channel = None;
|
||||
|
||||
if let Some(available_channels) = list.available_channels() {
|
||||
channel_count = available_channels.len();
|
||||
if self.active_channel.is_none() {
|
||||
if let Some(channel_id) = available_channels.first().map(|channel| channel.id) {
|
||||
active_channel = list.get_channel(channel_id, cx);
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
if let Some(channel) = channel {
|
||||
self.set_active_channel(channel, cx);
|
||||
} else {
|
||||
channel_count = 0;
|
||||
}
|
||||
} else if self.channel_list.read(cx).available_channels().is_none() {
|
||||
|
||||
(active_channel, channel_count)
|
||||
});
|
||||
|
||||
if let Some(active_channel) = active_channel {
|
||||
self.set_active_channel(active_channel, cx);
|
||||
} else {
|
||||
self.active_channel = None;
|
||||
}
|
||||
|
||||
self.channel_select.update(cx, |select, cx| {
|
||||
select.set_item_count(channel_count, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn set_active_channel(&mut self, channel: ModelHandle<Channel>, cx: &mut ViewContext<Self>) {
|
||||
@@ -106,28 +144,6 @@ impl ChatPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_channel_name(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let settings = self.settings.borrow();
|
||||
let theme = &settings.theme.chat_panel;
|
||||
if let Some((channel, _)) = self.active_channel.as_ref() {
|
||||
let channel = channel.read(cx);
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Container::new(
|
||||
Label::new("#".to_string(), theme.channel_name_hash.label.clone()).boxed(),
|
||||
)
|
||||
.with_style(&theme.channel_name_hash.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Label::new(channel.name().to_string(), theme.channel_name.clone()).boxed(),
|
||||
)
|
||||
.boxed()
|
||||
} else {
|
||||
Empty::new().boxed()
|
||||
}
|
||||
}
|
||||
|
||||
fn render_active_channel_messages(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let messages = if let Some((channel, _)) = self.active_channel.as_ref() {
|
||||
let channel = channel.read(cx);
|
||||
@@ -155,7 +171,7 @@ impl ChatPanel {
|
||||
Container::new(
|
||||
Label::new(
|
||||
message.sender.github_login.clone(),
|
||||
theme.sender.label.clone(),
|
||||
theme.sender.text.clone(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
@@ -166,7 +182,7 @@ impl ChatPanel {
|
||||
Container::new(
|
||||
Label::new(
|
||||
format_timestamp(message.timestamp, now),
|
||||
theme.timestamp.label.clone(),
|
||||
theme.timestamp.text.clone(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
@@ -185,6 +201,36 @@ impl ChatPanel {
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_channel_name(
|
||||
channel_list: &ModelHandle<ChannelList>,
|
||||
ix: usize,
|
||||
item_type: ItemType,
|
||||
is_hovered: bool,
|
||||
theme: &theme::ChannelSelect,
|
||||
cx: &AppContext,
|
||||
) -> ElementBox {
|
||||
let channel = &channel_list.read(cx).available_channels().unwrap()[ix];
|
||||
let theme = match (item_type, is_hovered) {
|
||||
(ItemType::Header, _) => &theme.header,
|
||||
(ItemType::Selected, false) => &theme.active_item,
|
||||
(ItemType::Selected, true) => &theme.hovered_active_item,
|
||||
(ItemType::Unselected, false) => &theme.item,
|
||||
(ItemType::Unselected, true) => &theme.hovered_item,
|
||||
};
|
||||
Container::new(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Container::new(Label::new("#".to_string(), theme.hash.text.clone()).boxed())
|
||||
.with_style(&theme.hash.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(Label::new(channel.name.clone(), theme.name.clone()).boxed())
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(&theme.container)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn send(&mut self, _: &Send, cx: &mut ViewContext<Self>) {
|
||||
if let Some((channel, _)) = self.active_channel.as_ref() {
|
||||
let body = self.input_editor.update(cx, |editor, cx| {
|
||||
@@ -224,7 +270,11 @@ impl View for ChatPanel {
|
||||
let theme = &self.settings.borrow().theme;
|
||||
Container::new(
|
||||
Flex::column()
|
||||
.with_child(self.render_channel_name(cx))
|
||||
.with_child(
|
||||
Container::new(ChildView::new(self.channel_select.id()).boxed())
|
||||
.with_style(&theme.chat_panel.channel_select.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(self.render_active_channel_messages(cx))
|
||||
.with_child(self.render_input_box())
|
||||
.boxed(),
|
||||
|
||||
+31
-4
@@ -68,15 +68,34 @@ pub struct ChatPanel {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub message: ChatMessage,
|
||||
pub channel_name: TextStyle,
|
||||
pub channel_name_hash: ContainedLabel,
|
||||
pub channel_select: ChannelSelect,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ChatMessage {
|
||||
pub body: TextStyle,
|
||||
pub sender: ContainedLabel,
|
||||
pub timestamp: ContainedLabel,
|
||||
pub sender: ContainedText,
|
||||
pub timestamp: ContainedText,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ChannelSelect {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub header: ChannelName,
|
||||
pub item: ChannelName,
|
||||
pub active_item: ChannelName,
|
||||
pub hovered_item: ChannelName,
|
||||
pub hovered_active_item: ChannelName,
|
||||
pub menu: ContainerStyle,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ChannelName {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub hash: ContainedText,
|
||||
pub name: TextStyle,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -90,6 +109,14 @@ pub struct Selector {
|
||||
pub active_item: ContainedLabel,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ContainedText {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
#[serde(flatten)]
|
||||
pub text: TextStyle,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ContainedLabel {
|
||||
#[serde(flatten)]
|
||||
|
||||
@@ -193,7 +193,7 @@ impl Pane {
|
||||
row.add_child(
|
||||
Expanded::new(
|
||||
1.0,
|
||||
MouseEventHandler::new::<Tab, _, _>(item.id(), cx, |mouse_state, cx| {
|
||||
MouseEventHandler::new::<Tab, _, _, _>(item.id(), cx, |mouse_state, cx| {
|
||||
let title = item.title(cx);
|
||||
|
||||
let mut border = border.clone();
|
||||
@@ -317,7 +317,7 @@ impl Pane {
|
||||
let close_color = current_color.unwrap_or(theme.workspace.tab.icon_close);
|
||||
let icon = Svg::new("icons/x.svg").with_color(close_color);
|
||||
|
||||
MouseEventHandler::new::<TabCloseButton, _, _>(item_id, cx, |mouse_state, _| {
|
||||
MouseEventHandler::new::<TabCloseButton, _, _, _>(item_id, cx, |mouse_state, _| {
|
||||
if mouse_state.hovered {
|
||||
Container::new(icon.with_color(Color::white()).boxed())
|
||||
.with_background_color(if mouse_state.clicked {
|
||||
|
||||
@@ -76,7 +76,7 @@ impl Sidebar {
|
||||
&settings.theme.workspace.sidebar_icon
|
||||
};
|
||||
enum SidebarButton {}
|
||||
MouseEventHandler::new::<SidebarButton, _, _>(item.view.id(), cx, |_, _| {
|
||||
MouseEventHandler::new::<SidebarButton, _, _, _>(item.view.id(), cx, |_, _| {
|
||||
ConstrainedBox::new(
|
||||
Align::new(
|
||||
ConstrainedBox::new(
|
||||
@@ -133,7 +133,7 @@ impl Sidebar {
|
||||
) -> ElementBox {
|
||||
let width = self.width.clone();
|
||||
let side = self.side;
|
||||
MouseEventHandler::new::<Self, _, _>(self.side.id(), &mut cx, |_, _| {
|
||||
MouseEventHandler::new::<Self, _, _, _>(self.side.id(), &mut cx, |_, _| {
|
||||
Container::new(Empty::new().boxed())
|
||||
.with_style(&settings.theme.workspace.sidebar.resize_handle)
|
||||
.boxed()
|
||||
|
||||
Reference in New Issue
Block a user