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
+38
-14
@@ -294,7 +294,9 @@ impl App {
|
||||
let platform = self.0.borrow().foreground_platform.clone();
|
||||
platform.run(Box::new(move || {
|
||||
let mut cx = self.0.borrow_mut();
|
||||
on_finish_launching(&mut *cx);
|
||||
let cx = &mut *cx;
|
||||
crate::views::init(cx);
|
||||
on_finish_launching(cx);
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1306,7 +1308,7 @@ impl MutableAppContext {
|
||||
|
||||
pub fn element_state<Tag: 'static, T: 'static + Default>(
|
||||
&mut self,
|
||||
id: usize,
|
||||
id: ElementStateId,
|
||||
) -> ElementStateHandle<T> {
|
||||
let key = (TypeId::of::<Tag>(), id);
|
||||
self.cx
|
||||
@@ -1699,7 +1701,7 @@ pub struct AppContext {
|
||||
models: HashMap<usize, Box<dyn AnyModel>>,
|
||||
views: HashMap<(usize, usize), Box<dyn AnyView>>,
|
||||
windows: HashMap<usize, Window>,
|
||||
element_states: HashMap<(TypeId, usize), Box<dyn Any>>,
|
||||
element_states: HashMap<(TypeId, ElementStateId), Box<dyn Any>>,
|
||||
background: Arc<executor::Background>,
|
||||
ref_counts: Arc<Mutex<RefCounts>>,
|
||||
font_cache: Arc<FontCache>,
|
||||
@@ -2977,6 +2979,10 @@ impl<T: View> WeakViewHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> usize {
|
||||
self.view_id
|
||||
}
|
||||
|
||||
pub fn upgrade(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
|
||||
if cx.ref_counts.lock().is_entity_alive(self.view_id) {
|
||||
Some(ViewHandle::new(
|
||||
@@ -3000,15 +3006,30 @@ impl<T> Clone for WeakViewHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ElementStateId(usize, usize);
|
||||
|
||||
impl From<usize> for ElementStateId {
|
||||
fn from(id: usize) -> Self {
|
||||
Self(id, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize)> for ElementStateId {
|
||||
fn from(id: (usize, usize)) -> Self {
|
||||
Self(id.0, id.1)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ElementStateHandle<T> {
|
||||
value_type: PhantomData<T>,
|
||||
tag_type_id: TypeId,
|
||||
id: usize,
|
||||
id: ElementStateId,
|
||||
ref_counts: Weak<Mutex<RefCounts>>,
|
||||
}
|
||||
|
||||
impl<T: 'static> ElementStateHandle<T> {
|
||||
fn new(tag_type_id: TypeId, id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
|
||||
fn new(tag_type_id: TypeId, id: ElementStateId, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
|
||||
ref_counts.lock().inc_element_state(tag_type_id, id);
|
||||
Self {
|
||||
value_type: PhantomData,
|
||||
@@ -3128,10 +3149,10 @@ impl Drop for Subscription {
|
||||
#[derive(Default)]
|
||||
struct RefCounts {
|
||||
entity_counts: HashMap<usize, usize>,
|
||||
element_state_counts: HashMap<(TypeId, usize), usize>,
|
||||
element_state_counts: HashMap<(TypeId, ElementStateId), usize>,
|
||||
dropped_models: HashSet<usize>,
|
||||
dropped_views: HashSet<(usize, usize)>,
|
||||
dropped_element_states: HashSet<(TypeId, usize)>,
|
||||
dropped_element_states: HashSet<(TypeId, ElementStateId)>,
|
||||
}
|
||||
|
||||
impl RefCounts {
|
||||
@@ -3155,11 +3176,14 @@ impl RefCounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn inc_element_state(&mut self, tag_type_id: TypeId, id: usize) {
|
||||
*self
|
||||
.element_state_counts
|
||||
.entry((tag_type_id, id))
|
||||
.or_insert(0) += 1;
|
||||
fn inc_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) {
|
||||
match self.element_state_counts.entry((tag_type_id, id)) {
|
||||
Entry::Occupied(mut entry) => *entry.get_mut() += 1,
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(1);
|
||||
self.dropped_element_states.remove(&(tag_type_id, id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dec_model(&mut self, model_id: usize) {
|
||||
@@ -3180,7 +3204,7 @@ impl RefCounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn dec_element_state(&mut self, tag_type_id: TypeId, id: usize) {
|
||||
fn dec_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) {
|
||||
let key = (tag_type_id, id);
|
||||
let count = self.element_state_counts.get_mut(&key).unwrap();
|
||||
*count -= 1;
|
||||
@@ -3199,7 +3223,7 @@ impl RefCounts {
|
||||
) -> (
|
||||
HashSet<usize>,
|
||||
HashSet<(usize, usize)>,
|
||||
HashSet<(TypeId, usize)>,
|
||||
HashSet<(TypeId, ElementStateId)>,
|
||||
) {
|
||||
let mut dropped_models = HashSet::new();
|
||||
let mut dropped_views = HashSet::new();
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::ops::DerefMut;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
platform::CursorStyle,
|
||||
CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext,
|
||||
LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
|
||||
CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateHandle, ElementStateId,
|
||||
Event, EventContext, LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -25,13 +25,14 @@ pub struct MouseState {
|
||||
}
|
||||
|
||||
impl MouseEventHandler {
|
||||
pub fn new<Tag, F, C>(id: usize, cx: &mut C, render_child: F) -> Self
|
||||
pub fn new<Tag, F, C, Id>(id: Id, cx: &mut C, render_child: F) -> Self
|
||||
where
|
||||
Tag: 'static,
|
||||
F: FnOnce(&MouseState, &mut C) -> ElementBox,
|
||||
C: DerefMut<Target = MutableAppContext>,
|
||||
Id: Into<ElementStateId>,
|
||||
{
|
||||
let state_handle = cx.element_state::<Tag, _>(id);
|
||||
let state_handle = cx.element_state::<Tag, _>(id.into());
|
||||
let child = state_handle.update(cx, |state, cx| render_child(state, cx));
|
||||
Self {
|
||||
state: state_handle,
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{self, json},
|
||||
AppContext, ElementBox,
|
||||
ElementBox, MutableAppContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use parking_lot::Mutex;
|
||||
@@ -38,7 +38,7 @@ pub struct LayoutState {
|
||||
|
||||
pub struct UniformList<F>
|
||||
where
|
||||
F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
|
||||
F: Fn(Range<usize>, &mut Vec<ElementBox>, &mut MutableAppContext),
|
||||
{
|
||||
state: UniformListState,
|
||||
item_count: usize,
|
||||
@@ -47,7 +47,7 @@ where
|
||||
|
||||
impl<F> UniformList<F>
|
||||
where
|
||||
F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
|
||||
F: Fn(Range<usize>, &mut Vec<ElementBox>, &mut MutableAppContext),
|
||||
{
|
||||
pub fn new(state: UniformListState, item_count: usize, append_items: F) -> Self {
|
||||
Self {
|
||||
@@ -102,7 +102,7 @@ where
|
||||
|
||||
impl<F> Element for UniformList<F>
|
||||
where
|
||||
F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
|
||||
F: Fn(Range<usize>, &mut Vec<ElementBox>, &mut MutableAppContext),
|
||||
{
|
||||
type LayoutState = LayoutState;
|
||||
type PaintState = ();
|
||||
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
mod select;
|
||||
|
||||
pub use select::*;
|
||||
pub use select::{ItemType, Select, SelectStyle};
|
||||
|
||||
pub fn init(cx: &mut super::MutableAppContext) {
|
||||
select::init(cx);
|
||||
}
|
||||
|
||||
+139
-6
@@ -1,14 +1,79 @@
|
||||
use crate::{elements::*, Entity, RenderContext, View};
|
||||
use std::ops::Range;
|
||||
use crate::{
|
||||
action, elements::*, AppContext, Entity, MutableAppContext, RenderContext, View, ViewContext,
|
||||
WeakViewHandle,
|
||||
};
|
||||
|
||||
pub struct Select {
|
||||
selected_ix: Option<usize>,
|
||||
render_selected_element: Box<dyn FnMut()>,
|
||||
render_elements: Box<dyn FnMut(Range<usize>, &mut RenderContext<Self>)>,
|
||||
handle: WeakViewHandle<Self>,
|
||||
render_item: Box<dyn Fn(usize, ItemType, bool, &AppContext) -> ElementBox>,
|
||||
selected_item_ix: usize,
|
||||
item_count: usize,
|
||||
is_open: bool,
|
||||
list_state: UniformListState,
|
||||
style: SelectStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectStyle {
|
||||
pub header: ContainerStyle,
|
||||
pub menu: ContainerStyle,
|
||||
}
|
||||
|
||||
pub enum ItemType {
|
||||
Header,
|
||||
Selected,
|
||||
Unselected,
|
||||
}
|
||||
|
||||
action!(ToggleSelect);
|
||||
action!(SelectItem, usize);
|
||||
|
||||
pub enum Event {}
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(Select::toggle);
|
||||
cx.add_action(Select::select_item);
|
||||
}
|
||||
|
||||
impl Select {
|
||||
pub fn new<F: 'static + Fn(usize, ItemType, bool, &AppContext) -> ElementBox>(
|
||||
item_count: usize,
|
||||
cx: &mut ViewContext<Self>,
|
||||
render_item: F,
|
||||
) -> Self {
|
||||
Self {
|
||||
handle: cx.handle().downgrade(),
|
||||
render_item: Box::new(render_item),
|
||||
selected_item_ix: 0,
|
||||
item_count,
|
||||
is_open: false,
|
||||
list_state: UniformListState::default(),
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_style(mut self, style: &SelectStyle) -> Self {
|
||||
self.style = style.clone();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_item_count(&mut self, count: usize, cx: &mut ViewContext<Self>) {
|
||||
self.item_count = count;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn toggle(&mut self, _: &ToggleSelect, cx: &mut ViewContext<Self>) {
|
||||
self.is_open = !self.is_open;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_item(&mut self, action: &SelectItem, cx: &mut ViewContext<Self>) {
|
||||
self.selected_item_ix = action.0;
|
||||
self.is_open = false;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Select {
|
||||
type Event = Event;
|
||||
}
|
||||
@@ -19,6 +84,74 @@ impl View for Select {
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
todo!()
|
||||
if self.item_count == 0 {
|
||||
return Empty::new().boxed();
|
||||
}
|
||||
|
||||
enum Header {}
|
||||
enum Item {}
|
||||
|
||||
let mut result = Flex::column().with_child(
|
||||
MouseEventHandler::new::<Header, _, _, _>(self.handle.id(), cx, |mouse_state, cx| {
|
||||
Container::new((self.render_item)(
|
||||
self.selected_item_ix,
|
||||
ItemType::Header,
|
||||
mouse_state.hovered,
|
||||
cx,
|
||||
))
|
||||
.with_style(&self.style.header)
|
||||
.boxed()
|
||||
})
|
||||
.on_click(move |cx| cx.dispatch_action(ToggleSelect))
|
||||
.boxed(),
|
||||
);
|
||||
if self.is_open {
|
||||
let handle = self.handle.clone();
|
||||
result.add_child(
|
||||
Overlay::new(
|
||||
Container::new(
|
||||
ConstrainedBox::new(
|
||||
UniformList::new(
|
||||
self.list_state.clone(),
|
||||
self.item_count,
|
||||
move |mut range, items, mut cx| {
|
||||
let handle = handle.upgrade(cx).unwrap();
|
||||
let this = handle.read(cx);
|
||||
let selected_item_ix = this.selected_item_ix;
|
||||
range.end = range.end.min(this.item_count);
|
||||
items.extend(range.map(|ix| {
|
||||
MouseEventHandler::new::<Item, _, _, _>(
|
||||
(handle.id(), ix),
|
||||
&mut cx,
|
||||
|mouse_state, cx| {
|
||||
(handle.read(*cx).render_item)(
|
||||
ix,
|
||||
if ix == selected_item_ix {
|
||||
ItemType::Selected
|
||||
} else {
|
||||
ItemType::Unselected
|
||||
},
|
||||
mouse_state.hovered,
|
||||
cx,
|
||||
)
|
||||
},
|
||||
)
|
||||
.on_click(move |cx| cx.dispatch_action(SelectItem(ix)))
|
||||
.boxed()
|
||||
}))
|
||||
},
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_max_height(200.)
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(&self.style.menu)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
}
|
||||
result.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user