Update Dropdown (#42)

This commit is contained in:
尹吉峰 2024-07-18 16:12:12 +08:00 committed by GitHub
parent 683b22c4a4
commit 4ccbe9bd71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 109 additions and 108 deletions

View file

@ -1,70 +1,53 @@
use std::rc::Rc; use std::borrow::Cow;
use gpui::{ use gpui::{
px, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext, WindowContext, px, IntoElement, ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext,
WindowContext,
}; };
use ui::{ use ui::{
dropdown::{Dropdown, DropdownDelegate, DropdownItem, StringDropdownDelegate}, dropdown::{Dropdown, DropdownItem},
h_flex, h_flex,
theme::ActiveTheme, theme::ActiveTheme,
v_flex, Selection, v_flex, Selection,
}; };
struct Country { struct Country {
name: &'static str, name: String,
code: &'static str, code: String,
} }
impl Country { impl Country {
pub fn new(name: &'static str, code: &'static str) -> Self { pub fn new(name: &str, code: &str) -> Self {
Self { name, code } Self {
name: name.to_string(),
code: code.to_string(),
}
} }
} }
impl DropdownItem for &Country { impl DropdownItem for Country {
fn title(&self) -> &str { type Value = String;
self.name
fn title(&self) -> Cow<'_, str> {
self.name.as_str().into()
} }
fn value(&self) -> &str { fn value(&self) -> &Self::Value {
self.code &self.code
} }
} }
struct CounterDelegate(Vec<Country>);
struct FuritDelegate(Vec<String>);
pub struct DropdownStory { pub struct DropdownStory {
country_dropdown: View<Dropdown<CounterDelegate>>, country_dropdown: View<Dropdown<Vec<Country>>>,
furit_dropdown: View<Dropdown<FuritDelegate>>, furit_dropdown: View<Dropdown<Vec<SharedString>>>,
simple_dropdown1: View<Dropdown<StringDropdownDelegate>>, simple_dropdown1: View<Dropdown<Vec<SharedString>>>,
simple_dropdown2: View<Dropdown<StringDropdownDelegate>>, simple_dropdown2: View<Dropdown<Vec<SharedString>>>,
}
impl DropdownDelegate for CounterDelegate {
fn len(&self) -> usize {
self.0.len()
}
fn get(&self, ix: usize) -> Option<impl DropdownItem> {
self.0.get(ix)
}
}
impl DropdownDelegate for FuritDelegate {
fn len(&self) -> usize {
self.0.len()
}
fn get(&self, ix: usize) -> Option<impl DropdownItem> {
self.0.get(ix)
}
} }
impl DropdownStory { impl DropdownStory {
pub fn new(cx: &mut WindowContext) -> View<Self> { pub fn new(cx: &mut WindowContext) -> View<Self> {
let countries = CounterDelegate(vec![ let countries = vec![
Country::new("United States", "US"), Country::new("United States", "US"),
Country::new("Canada", "CA"), Country::new("Canada", "CA"),
Country::new("Mexico", "MX"), Country::new("Mexico", "MX"),
@ -76,26 +59,22 @@ impl DropdownStory {
Country::new("Colombia", "CO"), Country::new("Colombia", "CO"),
Country::new("Venezuela", "VE"), Country::new("Venezuela", "VE"),
Country::new("Ecuador", "EC"), Country::new("Ecuador", "EC"),
]); ];
let country_dropdown = let country_dropdown =
cx.new_view(|cx| Dropdown::new("dropdown-country", countries, Some(6), cx)); cx.new_view(|cx| Dropdown::new("dropdown-country", countries, Some(6), cx));
let furits = FuritDelegate( let furits = vec![
[ "Apple",
"Apple", "Orange",
"Orange", "Banana",
"Banana", "Grape",
"Grape", "Pineapple",
"Pineapple", "Watermelon",
"Watermelon", "Avocado",
"Avocado", ];
] let furit_dropdown =
.iter() cx.new_view(|cx| Dropdown::string_list("dropdown-furits", furits, None, cx));
.map(|s| s.to_string())
.collect(),
);
let furit_dropdown = cx.new_view(|cx| Dropdown::new("dropdown-furits", furits, None, cx));
cx.new_view(|cx| Self { cx.new_view(|cx| Self {
country_dropdown, country_dropdown,
@ -103,12 +82,7 @@ impl DropdownStory {
simple_dropdown1: cx.new_view(|cx| { simple_dropdown1: cx.new_view(|cx| {
Dropdown::string_list( Dropdown::string_list(
"string-list1", "string-list1",
Rc::new(vec![ vec!["QPUI", "Iced", "QT", "Cocoa"],
"QPUI".into(),
"Iced".into(),
"QT".into(),
"Cocoa".into(),
]),
Some(0), Some(0),
cx, cx,
) )
@ -119,12 +93,7 @@ impl DropdownStory {
simple_dropdown2: cx.new_view(|cx| { simple_dropdown2: cx.new_view(|cx| {
Dropdown::string_list( Dropdown::string_list(
"string-list2", "string-list2",
Rc::new(vec![ vec!["Rust", "Go", "C++", "JavaScript"],
"Rust".into(),
"Go".into(),
"C++".into(),
"JavaScript".into(),
]),
None, None,
cx, cx,
) )

View file

@ -1,4 +1,4 @@
use std::rc::Rc; use std::borrow::Cow;
use gpui::{ use gpui::{
actions, deferred, div, prelude::FluentBuilder as _, px, rems, AnyElement, AppContext, actions, deferred, div, prelude::FluentBuilder as _, px, rems, AnyElement, AppContext,
@ -31,36 +31,73 @@ use crate::{
/// A trait for items that can be displayed in a dropdown. /// A trait for items that can be displayed in a dropdown.
pub trait DropdownItem { pub trait DropdownItem {
fn title(&self) -> &str; type Value: Clone;
fn value(&self) -> &str; fn title(&self) -> Cow<'_, str>;
fn value(&self) -> &Self::Value;
} }
impl DropdownItem for &String { impl DropdownItem for String {
fn title(&self) -> &str { type Value = Self;
self
fn title(&self) -> Cow<'_, str> {
self.as_str().into()
} }
fn value(&self) -> &str { fn value(&self) -> &Self::Value {
self &self
} }
} }
impl DropdownItem for &SharedString { impl DropdownItem for SharedString {
fn title(&self) -> &str { type Value = Self;
self.as_ref()
fn title(&self) -> Cow<'_, str> {
self.as_ref().into()
} }
fn value(&self) -> &str { fn value(&self) -> &Self::Value {
self.as_ref() &self
} }
} }
pub trait DropdownDelegate { pub trait DropdownDelegate {
type Item: DropdownItem;
fn len(&self) -> usize; fn len(&self) -> usize;
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
fn get(&self, ix: usize) -> Option<impl DropdownItem>;
fn get(&self, ix: usize) -> Option<&Self::Item>;
fn position<V>(&self, value: &V) -> Option<usize>
where
Self::Item: DropdownItem<Value = V>,
V: PartialEq,
{
(0..self.len()).find(|&i| self.get(i).map_or(false, |item| item.value() == value))
}
}
impl<T: DropdownItem> DropdownDelegate for Vec<T> {
type Item = T;
fn len(&self) -> usize {
self.len()
}
fn get(&self, ix: usize) -> Option<&Self::Item> {
self.as_slice().get(ix)
}
fn position<V>(&self, value: &V) -> Option<usize>
where
Self::Item: DropdownItem<Value = V>,
V: PartialEq,
{
self.iter().position(|v| v.value() == value)
}
} }
struct DropdownListDelegate<D: DropdownDelegate + 'static> { struct DropdownListDelegate<D: DropdownDelegate + 'static> {
@ -121,7 +158,7 @@ where
view.selected_value = self view.selected_value = self
.selected_index .selected_index
.and_then(|ix| self.delegate.get(ix)) .and_then(|ix| self.delegate.get(ix))
.map(|item| item.value().to_string().into()); .map(|item| item.value().clone());
view.open = false; view.open = false;
}); });
} }
@ -132,20 +169,6 @@ where
} }
} }
pub struct StringDropdownDelegate {
items: Rc<Vec<SharedString>>,
}
impl DropdownDelegate for StringDropdownDelegate {
fn len(&self) -> usize {
self.items.len()
}
fn get(&self, ix: usize) -> Option<impl DropdownItem> {
self.items.get(ix)
}
}
pub struct Dropdown<D: DropdownDelegate + 'static> { pub struct Dropdown<D: DropdownDelegate + 'static> {
id: ElementId, id: ElementId,
focus_handle: FocusHandle, focus_handle: FocusHandle,
@ -155,7 +178,7 @@ pub struct Dropdown<D: DropdownDelegate + 'static> {
cleanable: bool, cleanable: bool,
placeholder: SharedString, placeholder: SharedString,
title_prefix: Option<SharedString>, title_prefix: Option<SharedString>,
selected_value: Option<SharedString>, selected_value: Option<<D::Item as DropdownItem>::Value>,
} }
impl<D> Dropdown<D> impl<D> Dropdown<D>
@ -228,6 +251,18 @@ where
self.update_selected_value(cx); self.update_selected_value(cx);
} }
pub fn set_selected_value(
&mut self,
selected_value: &<D::Item as DropdownItem>::Value,
cx: &mut ViewContext<Self>,
) where
<<D as DropdownDelegate>::Item as DropdownItem>::Value: PartialEq,
{
let delegate = self.list.read(cx).delegate();
let selected_index = delegate.delegate.position(selected_value);
self.set_selected_index(selected_index, cx);
}
pub fn selected_index(&self, cx: &WindowContext) -> Option<usize> { pub fn selected_index(&self, cx: &WindowContext) -> Option<usize> {
self.list.read(cx).selected_index() self.list.read(cx).selected_index()
} }
@ -236,11 +271,11 @@ where
self.selected_value = self self.selected_value = self
.selected_index(cx) .selected_index(cx)
.and_then(|ix| self.list.read(cx).delegate().delegate.get(ix)) .and_then(|ix| self.list.read(cx).delegate().delegate.get(ix))
.map(|item| item.value().to_string().into()); .map(|item| item.value().clone());
} }
pub fn selected_value(&self) -> Option<SharedString> { pub fn selected_value(&self) -> Option<&<D::Item as DropdownItem>::Value> {
self.selected_value.clone() self.selected_value.as_ref()
} }
fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) { fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
@ -326,18 +361,15 @@ where
} }
} }
impl Dropdown<StringDropdownDelegate> { impl Dropdown<Vec<SharedString>> {
pub fn string_list( pub fn string_list(
id: impl Into<ElementId>, id: impl Into<ElementId>,
items: Rc<Vec<SharedString>>, items: Vec<impl Into<SharedString>>,
selected_index: Option<usize>, selected_index: Option<usize>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
let delegate = StringDropdownDelegate { let items = items.into_iter().map(Into::into).collect();
items: items.clone(), Self::new(id, items, selected_index, cx)
};
Self::new(id, delegate, selected_index, cx)
} }
} }