mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 10:29:35 +03:00
Re-render all list elements when refreshing windows
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
5fe5685641
commit
4388c45029
@ -1305,7 +1305,7 @@ impl MutableAppContext {
|
||||
if !self.flushing_effects && self.pending_flushes == 0 {
|
||||
self.flushing_effects = true;
|
||||
|
||||
let mut full_refresh = false;
|
||||
let mut refreshing = false;
|
||||
loop {
|
||||
if let Some(effect) = self.pending_effects.pop_front() {
|
||||
match effect {
|
||||
@ -1320,13 +1320,13 @@ impl MutableAppContext {
|
||||
self.focus(window_id, view_id);
|
||||
}
|
||||
Effect::RefreshWindows => {
|
||||
full_refresh = true;
|
||||
refreshing = true;
|
||||
}
|
||||
}
|
||||
self.remove_dropped_entities();
|
||||
} else {
|
||||
self.remove_dropped_entities();
|
||||
if full_refresh {
|
||||
if refreshing {
|
||||
self.perform_window_refresh();
|
||||
} else {
|
||||
self.update_windows();
|
||||
@ -1336,7 +1336,7 @@ impl MutableAppContext {
|
||||
self.flushing_effects = false;
|
||||
break;
|
||||
} else {
|
||||
full_refresh = false;
|
||||
refreshing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1638,11 +1638,11 @@ impl AppContext {
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
titlebar_height: f32,
|
||||
refresh: bool,
|
||||
refreshing: bool,
|
||||
) -> Result<ElementBox> {
|
||||
self.views
|
||||
.get(&(window_id, view_id))
|
||||
.map(|v| v.render(window_id, view_id, titlebar_height, refresh, self))
|
||||
.map(|v| v.render(window_id, view_id, titlebar_height, refreshing, self))
|
||||
.ok_or(anyhow!("view not found"))
|
||||
}
|
||||
|
||||
@ -1666,6 +1666,23 @@ impl AppContext {
|
||||
.collect::<HashMap<_, ElementBox>>()
|
||||
}
|
||||
|
||||
pub fn render_cx<V: View>(
|
||||
&self,
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
titlebar_height: f32,
|
||||
refreshing: bool,
|
||||
) -> RenderContext<V> {
|
||||
RenderContext {
|
||||
app: self,
|
||||
titlebar_height,
|
||||
refreshing,
|
||||
window_id,
|
||||
view_id,
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn background(&self) -> &Arc<executor::Background> {
|
||||
&self.background
|
||||
}
|
||||
@ -1816,7 +1833,7 @@ pub trait AnyView {
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
titlebar_height: f32,
|
||||
refresh: bool,
|
||||
refreshing: bool,
|
||||
cx: &AppContext,
|
||||
) -> ElementBox;
|
||||
fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
|
||||
@ -1849,7 +1866,7 @@ where
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
titlebar_height: f32,
|
||||
refresh: bool,
|
||||
refreshing: bool,
|
||||
cx: &AppContext,
|
||||
) -> ElementBox {
|
||||
View::render(
|
||||
@ -1860,7 +1877,7 @@ where
|
||||
app: cx,
|
||||
view_type: PhantomData::<T>,
|
||||
titlebar_height,
|
||||
refresh,
|
||||
refreshing,
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -2229,7 +2246,7 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||
pub struct RenderContext<'a, T: View> {
|
||||
pub app: &'a AppContext,
|
||||
pub titlebar_height: f32,
|
||||
pub refresh: bool,
|
||||
pub refreshing: bool,
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
view_type: PhantomData<T>,
|
||||
@ -2255,6 +2272,12 @@ impl<V: View> Deref for RenderContext<'_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ReadModel for RenderContext<'_, V> {
|
||||
fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
|
||||
self.app.read_model(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> AsRef<AppContext> for ViewContext<'_, M> {
|
||||
fn as_ref(&self) -> &AppContext {
|
||||
&self.app.cx
|
||||
|
@ -37,7 +37,14 @@ use crate::{
|
||||
};
|
||||
use core::panic;
|
||||
use json::ToJson;
|
||||
use std::{any::Any, borrow::Cow, mem};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
cell::RefCell,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
trait AnyElement {
|
||||
fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F;
|
||||
@ -91,20 +98,20 @@ pub trait Element {
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
ElementBox {
|
||||
ElementBox(ElementRc {
|
||||
name: None,
|
||||
element: Box::new(Lifecycle::Init { element: self }),
|
||||
}
|
||||
element: Rc::new(RefCell::new(Lifecycle::Init { element: self })),
|
||||
})
|
||||
}
|
||||
|
||||
fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
ElementBox {
|
||||
ElementBox(ElementRc {
|
||||
name: Some(name.into()),
|
||||
element: Box::new(Lifecycle::Init { element: self }),
|
||||
}
|
||||
element: Rc::new(RefCell::new(Lifecycle::Init { element: self })),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,9 +134,12 @@ pub enum Lifecycle<T: Element> {
|
||||
paint: T::PaintState,
|
||||
},
|
||||
}
|
||||
pub struct ElementBox {
|
||||
pub struct ElementBox(ElementRc);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ElementRc {
|
||||
name: Option<Cow<'static, str>>,
|
||||
element: Box<dyn AnyElement>,
|
||||
element: Rc<RefCell<dyn AnyElement>>,
|
||||
}
|
||||
|
||||
impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
@ -262,28 +272,51 @@ impl<T: Element> Default for Lifecycle<T> {
|
||||
}
|
||||
|
||||
impl ElementBox {
|
||||
pub fn metadata(&self) -> Option<&dyn Any> {
|
||||
let element = unsafe { &*self.0.element.as_ptr() };
|
||||
element.metadata()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ElementRc> for ElementBox {
|
||||
fn into(self) -> ElementRc {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ElementBox {
|
||||
type Target = ElementRc;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ElementBox {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementRc {
|
||||
pub fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F {
|
||||
self.element.layout(constraint, cx)
|
||||
self.element.borrow_mut().layout(constraint, cx)
|
||||
}
|
||||
|
||||
pub fn paint(&mut self, origin: Vector2F, cx: &mut PaintContext) {
|
||||
self.element.paint(origin, cx);
|
||||
self.element.borrow_mut().paint(origin, cx);
|
||||
}
|
||||
|
||||
pub fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool {
|
||||
self.element.dispatch_event(event, cx)
|
||||
self.element.borrow_mut().dispatch_event(event, cx)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vector2F {
|
||||
self.element.size()
|
||||
}
|
||||
|
||||
pub fn metadata(&self) -> Option<&dyn Any> {
|
||||
self.element.metadata()
|
||||
self.element.borrow().size()
|
||||
}
|
||||
|
||||
pub fn debug(&self, cx: &DebugContext) -> json::Value {
|
||||
let mut value = self.element.debug(cx);
|
||||
let mut value = self.element.borrow().debug(cx);
|
||||
|
||||
if let Some(name) = &self.name {
|
||||
if let json::Value::Object(map) = &mut value {
|
||||
|
@ -5,19 +5,17 @@ use crate::{
|
||||
},
|
||||
json::json,
|
||||
sum_tree::{self, Bias, SumTree},
|
||||
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
DebugContext, Element, ElementBox, ElementRc, Event, EventContext, LayoutContext, PaintContext,
|
||||
RenderContext, SizeConstraint, View,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
use crate::ElementBox;
|
||||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||
|
||||
pub struct List {
|
||||
state: ListState,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ListState(Arc<Mutex<StateInner>>);
|
||||
pub struct ListState(Rc<RefCell<StateInner>>);
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum Orientation {
|
||||
@ -27,7 +25,7 @@ pub enum Orientation {
|
||||
|
||||
struct StateInner {
|
||||
last_layout_width: f32,
|
||||
elements: Vec<ElementBox>,
|
||||
elements: Vec<Option<ElementRc>>,
|
||||
heights: SumTree<ElementHeight>,
|
||||
scroll_position: f32,
|
||||
orientation: Orientation,
|
||||
@ -56,13 +54,55 @@ struct PendingCount(usize);
|
||||
struct Height(f32);
|
||||
|
||||
impl List {
|
||||
pub fn new(state: ListState) -> Self {
|
||||
pub fn new<F, I, V>(state: ListState, cx: &RenderContext<V>, build_items: F) -> Self
|
||||
where
|
||||
F: Fn(Range<usize>) -> I,
|
||||
I: IntoIterator<Item = ElementBox>,
|
||||
V: View,
|
||||
{
|
||||
{
|
||||
let state = &mut *state.0.borrow_mut();
|
||||
if cx.refreshing {
|
||||
let elements = (build_items)(0..state.elements.len());
|
||||
state.elements.clear();
|
||||
state
|
||||
.elements
|
||||
.extend(elements.into_iter().map(|e| Some(e.into())));
|
||||
state.heights = SumTree::new();
|
||||
state.heights.extend(
|
||||
(0..state.elements.len()).map(|_| ElementHeight::Pending),
|
||||
&(),
|
||||
);
|
||||
} else {
|
||||
let mut cursor = state.heights.cursor::<PendingCount, Count>();
|
||||
cursor.seek(&PendingCount(1), sum_tree::Bias::Left, &());
|
||||
|
||||
while cursor.item().is_some() {
|
||||
let start_ix = cursor.sum_start().0;
|
||||
while cursor.item().map_or(false, |h| h.is_pending()) {
|
||||
cursor.next(&());
|
||||
}
|
||||
let end_ix = cursor.sum_start().0;
|
||||
if end_ix > start_ix {
|
||||
state.elements.splice(
|
||||
start_ix..end_ix,
|
||||
(build_items)(start_ix..end_ix)
|
||||
.into_iter()
|
||||
.map(|e| Some(e.into())),
|
||||
);
|
||||
}
|
||||
|
||||
cursor.seek(&PendingCount(cursor.seek_start().0 + 1), Bias::Left, &());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self { state }
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for List {
|
||||
type LayoutState = ();
|
||||
type LayoutState = Vec<ElementRc>;
|
||||
|
||||
type PaintState = ();
|
||||
|
||||
@ -71,7 +111,7 @@ impl Element for List {
|
||||
constraint: SizeConstraint,
|
||||
cx: &mut LayoutContext,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let state = &mut *self.state.0.lock();
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
let mut item_constraint = constraint;
|
||||
item_constraint.min.set_y(0.);
|
||||
item_constraint.max.set_y(f32::INFINITY);
|
||||
@ -87,8 +127,8 @@ impl Element for List {
|
||||
|
||||
while let Some(height) = old_heights.item() {
|
||||
if height.is_pending() {
|
||||
let size =
|
||||
state.elements[old_heights.sum_start().count].layout(item_constraint, cx);
|
||||
let element = &mut state.elements[old_heights.sum_start().count];
|
||||
let size = element.as_mut().unwrap().layout(item_constraint, cx);
|
||||
new_heights.push(ElementHeight::Ready(size.y()), &());
|
||||
|
||||
// Adjust scroll position to keep visible elements stable
|
||||
@ -123,18 +163,28 @@ impl Element for List {
|
||||
} else {
|
||||
state.heights = SumTree::new();
|
||||
for element in &mut state.elements {
|
||||
let element = element.as_mut().unwrap();
|
||||
let size = element.layout(item_constraint, cx);
|
||||
state.heights.push(ElementHeight::Ready(size.y()), &());
|
||||
}
|
||||
state.last_layout_width = constraint.max.x();
|
||||
}
|
||||
|
||||
(size, ())
|
||||
let visible_elements = state.elements[state.visible_range(size.y())]
|
||||
.iter()
|
||||
.map(|e| e.clone().unwrap())
|
||||
.collect();
|
||||
(size, visible_elements)
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: RectF, _: &mut (), cx: &mut PaintContext) {
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: RectF,
|
||||
visible_elements: &mut Self::LayoutState,
|
||||
cx: &mut PaintContext,
|
||||
) {
|
||||
cx.scene.push_layer(Some(bounds));
|
||||
let state = &mut *self.state.0.lock();
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
let visible_range = state.visible_range(bounds.height());
|
||||
|
||||
let mut item_top = {
|
||||
@ -149,7 +199,7 @@ impl Element for List {
|
||||
}
|
||||
let scroll_top = state.scroll_top(bounds.height());
|
||||
|
||||
for element in &mut state.elements[visible_range] {
|
||||
for element in visible_elements {
|
||||
let origin = bounds.origin() + vec2f(0., item_top - scroll_top);
|
||||
element.paint(origin, cx);
|
||||
item_top += element.size().y();
|
||||
@ -161,16 +211,15 @@ impl Element for List {
|
||||
&mut self,
|
||||
event: &Event,
|
||||
bounds: RectF,
|
||||
_: &mut (),
|
||||
visible_elements: &mut Self::LayoutState,
|
||||
_: &mut (),
|
||||
cx: &mut EventContext,
|
||||
) -> bool {
|
||||
let mut handled = false;
|
||||
|
||||
let mut state = self.state.0.lock();
|
||||
let visible_range = state.visible_range(bounds.height());
|
||||
for item in &mut state.elements[visible_range] {
|
||||
handled = item.dispatch_event(event, cx) || handled;
|
||||
let mut state = self.state.0.borrow_mut();
|
||||
for element in visible_elements {
|
||||
handled = element.dispatch_event(event, cx) || handled;
|
||||
}
|
||||
|
||||
match event {
|
||||
@ -191,10 +240,16 @@ impl Element for List {
|
||||
handled
|
||||
}
|
||||
|
||||
fn debug(&self, bounds: RectF, _: &(), _: &(), cx: &DebugContext) -> serde_json::Value {
|
||||
let state = self.state.0.lock();
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
visible_elements: &Self::LayoutState,
|
||||
_: &(),
|
||||
cx: &DebugContext,
|
||||
) -> serde_json::Value {
|
||||
let state = self.state.0.borrow_mut();
|
||||
let visible_range = state.visible_range(bounds.height());
|
||||
let visible_elements = state.elements[visible_range.clone()]
|
||||
let visible_elements = visible_elements
|
||||
.iter()
|
||||
.map(|e| e.debug(cx))
|
||||
.collect::<Vec<_>>();
|
||||
@ -207,40 +262,29 @@ impl Element for List {
|
||||
}
|
||||
|
||||
impl ListState {
|
||||
pub fn new(elements: Vec<ElementBox>, orientation: Orientation) -> Self {
|
||||
pub fn new(element_count: usize, orientation: Orientation) -> Self {
|
||||
let mut heights = SumTree::new();
|
||||
heights.extend(elements.iter().map(|_| ElementHeight::Pending), &());
|
||||
Self(Arc::new(Mutex::new(StateInner {
|
||||
heights.extend((0..element_count).map(|_| ElementHeight::Pending), &());
|
||||
Self(Rc::new(RefCell::new(StateInner {
|
||||
last_layout_width: 0.,
|
||||
elements,
|
||||
elements: (0..element_count).map(|_| None).collect(),
|
||||
heights,
|
||||
scroll_position: 0.,
|
||||
orientation,
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn splice(
|
||||
&self,
|
||||
old_range: Range<usize>,
|
||||
new_elements: impl IntoIterator<Item = ElementBox>,
|
||||
) {
|
||||
let state = &mut *self.0.lock();
|
||||
pub fn splice(&self, old_range: Range<usize>, count: usize) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
let mut old_heights = state.heights.cursor::<Count, ()>();
|
||||
let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
|
||||
old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
|
||||
|
||||
let mut len = 0;
|
||||
let old_elements = state.elements.splice(
|
||||
old_range,
|
||||
new_elements.into_iter().map(|e| {
|
||||
len += 1;
|
||||
e
|
||||
}),
|
||||
);
|
||||
let old_elements = state.elements.splice(old_range, (0..count).map(|_| None));
|
||||
drop(old_elements);
|
||||
|
||||
new_heights.extend((0..len).map(|_| ElementHeight::Pending), &());
|
||||
new_heights.extend((0..count).map(|_| ElementHeight::Pending), &());
|
||||
new_heights.push_tree(old_heights.suffix(&()), &());
|
||||
drop(old_heights);
|
||||
state.heights = new_heights;
|
||||
@ -370,22 +414,28 @@ impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for Height {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{elements::*, geometry::vector::vec2f};
|
||||
use crate::{elements::*, geometry::vector::vec2f, Entity};
|
||||
|
||||
#[crate::test(self)]
|
||||
fn test_layout(cx: &mut crate::MutableAppContext) {
|
||||
let mut presenter = cx.build_presenter(0, 20.0);
|
||||
let mut layout_cx = presenter.layout_cx(cx);
|
||||
let state = ListState::new(vec![item(20.), item(30.), item(10.)], Orientation::Top);
|
||||
let mut list = List::new(state.clone()).boxed();
|
||||
let mut presenter = cx.build_presenter(0, 0.);
|
||||
|
||||
let mut elements = vec![20., 30., 10.];
|
||||
let state = ListState::new(elements.len(), Orientation::Top);
|
||||
|
||||
let mut list = List::new(
|
||||
state.clone(),
|
||||
&cx.render_cx::<TestView>(0, 0, 0., false),
|
||||
|range| elements[range].iter().copied().map(item),
|
||||
)
|
||||
.boxed();
|
||||
let size = list.layout(
|
||||
SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)),
|
||||
&mut layout_cx,
|
||||
&mut presenter.layout_cx(cx),
|
||||
);
|
||||
assert_eq!(size, vec2f(100., 40.));
|
||||
assert_eq!(
|
||||
state.0.lock().heights.summary(),
|
||||
state.0.borrow().heights.summary(),
|
||||
ElementHeightSummary {
|
||||
count: 3,
|
||||
pending_count: 0,
|
||||
@ -393,23 +443,32 @@ mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
state.splice(1..2, vec![item(40.), item(50.)]);
|
||||
state.splice(3..3, vec![item(60.)]);
|
||||
elements.splice(1..2, vec![40., 50.]);
|
||||
elements.push(60.);
|
||||
state.splice(1..2, 2);
|
||||
state.splice(4..4, 1);
|
||||
assert_eq!(
|
||||
state.0.lock().heights.summary(),
|
||||
state.0.borrow().heights.summary(),
|
||||
ElementHeightSummary {
|
||||
count: 5,
|
||||
pending_count: 3,
|
||||
height: 30.
|
||||
}
|
||||
);
|
||||
|
||||
let mut list = List::new(
|
||||
state.clone(),
|
||||
&cx.render_cx::<TestView>(0, 0, 0., false),
|
||||
|range| elements[range].iter().copied().map(item),
|
||||
)
|
||||
.boxed();
|
||||
let size = list.layout(
|
||||
SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)),
|
||||
&mut layout_cx,
|
||||
&mut presenter.layout_cx(cx),
|
||||
);
|
||||
assert_eq!(size, vec2f(100., 40.));
|
||||
assert_eq!(
|
||||
state.0.lock().heights.summary(),
|
||||
state.0.borrow().heights.summary(),
|
||||
ElementHeightSummary {
|
||||
count: 5,
|
||||
pending_count: 0,
|
||||
@ -424,4 +483,20 @@ mod tests {
|
||||
.with_width(100.)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
struct TestView;
|
||||
|
||||
impl Entity for TestView {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for TestView {
|
||||
fn ui_name() -> &'static str {
|
||||
"TestView"
|
||||
}
|
||||
|
||||
fn render(&self, _: &RenderContext<'_, Self>) -> ElementBox {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ pub use scene::{Border, Quad, Scene};
|
||||
pub mod text_layout;
|
||||
pub use text_layout::TextLayoutCache;
|
||||
mod util;
|
||||
pub use elements::{Element, ElementBox};
|
||||
pub use elements::{Element, ElementBox, ElementRc};
|
||||
pub mod executor;
|
||||
pub use executor::Task;
|
||||
pub mod color;
|
||||
|
@ -60,7 +60,7 @@ pub struct PendingChannelMessage {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ChannelMessageSummary {
|
||||
max_id: u64,
|
||||
count: Count,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
@ -208,7 +208,7 @@ impl Channel {
|
||||
}
|
||||
|
||||
channel.update(&mut cx, |channel, cx| {
|
||||
let old_count = channel.messages.summary().count.0;
|
||||
let old_count = channel.messages.summary().count;
|
||||
let new_count = messages.len();
|
||||
|
||||
channel.messages = SumTree::new();
|
||||
@ -282,6 +282,10 @@ impl Channel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn message_count(&self) -> usize {
|
||||
self.messages.summary().count
|
||||
}
|
||||
|
||||
pub fn messages(&self) -> &SumTree<ChannelMessage> {
|
||||
&self.messages
|
||||
}
|
||||
@ -380,7 +384,7 @@ impl sum_tree::Item for ChannelMessage {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
ChannelMessageSummary {
|
||||
max_id: self.id,
|
||||
count: Count(1),
|
||||
count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,7 +394,7 @@ impl sum_tree::Summary for ChannelMessageSummary {
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
||||
self.max_id = summary.max_id;
|
||||
self.count.0 += summary.count.0;
|
||||
self.count += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,7 +407,7 @@ impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for u64 {
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for Count {
|
||||
fn add_summary(&mut self, summary: &'a ChannelMessageSummary, _: &()) {
|
||||
self.0 += summary.count.0;
|
||||
self.0 += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ use time::{OffsetDateTime, UtcOffset};
|
||||
pub struct ChatPanel {
|
||||
channel_list: ModelHandle<ChannelList>,
|
||||
active_channel: Option<(ModelHandle<Channel>, Subscription)>,
|
||||
messages: ListState,
|
||||
message_list: ListState,
|
||||
input_editor: ViewHandle<Editor>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
}
|
||||
@ -38,8 +38,8 @@ impl ChatPanel {
|
||||
let input_editor = cx.add_view(|cx| Editor::auto_height(settings.clone(), cx));
|
||||
let mut this = Self {
|
||||
channel_list,
|
||||
active_channel: None,
|
||||
messages: ListState::new(Vec::new(), Orientation::Bottom),
|
||||
active_channel: Default::default(),
|
||||
message_list: ListState::new(0, Orientation::Bottom),
|
||||
input_editor,
|
||||
settings,
|
||||
};
|
||||
@ -76,23 +76,15 @@ impl ChatPanel {
|
||||
fn set_active_channel(&mut self, channel: ModelHandle<Channel>, cx: &mut ViewContext<Self>) {
|
||||
if self.active_channel.as_ref().map(|e| &e.0) != Some(&channel) {
|
||||
let subscription = cx.subscribe(&channel, Self::channel_did_change);
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.messages = ListState::new(
|
||||
channel
|
||||
.read(cx)
|
||||
.messages()
|
||||
.cursor::<(), ()>()
|
||||
.map(|m| self.render_message(m, now))
|
||||
.collect(),
|
||||
Orientation::Bottom,
|
||||
);
|
||||
self.message_list =
|
||||
ListState::new(channel.read(cx).message_count(), Orientation::Bottom);
|
||||
self.active_channel = Some((channel, subscription));
|
||||
}
|
||||
}
|
||||
|
||||
fn channel_did_change(
|
||||
&mut self,
|
||||
channel: ModelHandle<Channel>,
|
||||
_: ModelHandle<Channel>,
|
||||
event: &ChannelEvent,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
@ -101,21 +93,27 @@ impl ChatPanel {
|
||||
old_range,
|
||||
new_count,
|
||||
} => {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.messages.splice(
|
||||
old_range.clone(),
|
||||
channel
|
||||
.read(cx)
|
||||
.messages_in_range(old_range.start..(old_range.start + new_count))
|
||||
.map(|message| self.render_message(message, now)),
|
||||
);
|
||||
self.message_list.splice(old_range.clone(), *new_count);
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_active_channel_messages(&self) -> ElementBox {
|
||||
Expanded::new(1., List::new(self.messages.clone()).boxed()).boxed()
|
||||
fn render_active_channel_messages(&self, cx: &RenderContext<Self>) -> ElementBox {
|
||||
let messages = if let Some((channel, _)) = self.active_channel.as_ref() {
|
||||
let channel = channel.read(cx);
|
||||
let now = OffsetDateTime::now_utc();
|
||||
List::new(self.message_list.clone(), cx, |range| {
|
||||
channel
|
||||
.messages_in_range(range)
|
||||
.map(|message| self.render_message(message, now))
|
||||
})
|
||||
.boxed()
|
||||
} else {
|
||||
Empty::new().boxed()
|
||||
};
|
||||
|
||||
Expanded::new(1., messages).boxed()
|
||||
}
|
||||
|
||||
fn render_message(&self, message: &ChannelMessage, now: OffsetDateTime) -> ElementBox {
|
||||
@ -194,11 +192,11 @@ impl View for ChatPanel {
|
||||
"ChatPanel"
|
||||
}
|
||||
|
||||
fn render(&self, _: &RenderContext<Self>) -> ElementBox {
|
||||
fn render(&self, cx: &RenderContext<Self>) -> ElementBox {
|
||||
let theme = &self.settings.borrow().theme;
|
||||
Container::new(
|
||||
Flex::column()
|
||||
.with_child(self.render_active_channel_messages())
|
||||
.with_child(self.render_active_channel_messages(cx))
|
||||
.with_child(self.render_input_box())
|
||||
.boxed(),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user