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

View file

@ -1,4 +1,4 @@
use std::rc::Rc;
use std::borrow::Cow;
use gpui::{
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.
pub trait DropdownItem {
fn title(&self) -> &str;
fn value(&self) -> &str;
type Value: Clone;
fn title(&self) -> Cow<'_, str>;
fn value(&self) -> &Self::Value;
}
impl DropdownItem for &String {
fn title(&self) -> &str {
self
impl DropdownItem for String {
type Value = Self;
fn title(&self) -> Cow<'_, str> {
self.as_str().into()
}
fn value(&self) -> &str {
self
fn value(&self) -> &Self::Value {
&self
}
}
impl DropdownItem for &SharedString {
fn title(&self) -> &str {
self.as_ref()
impl DropdownItem for SharedString {
type Value = Self;
fn title(&self) -> Cow<'_, str> {
self.as_ref().into()
}
fn value(&self) -> &str {
self.as_ref()
fn value(&self) -> &Self::Value {
&self
}
}
pub trait DropdownDelegate {
type Item: DropdownItem;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
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> {
@ -121,7 +158,7 @@ where
view.selected_value = self
.selected_index
.and_then(|ix| self.delegate.get(ix))
.map(|item| item.value().to_string().into());
.map(|item| item.value().clone());
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> {
id: ElementId,
focus_handle: FocusHandle,
@ -155,7 +178,7 @@ pub struct Dropdown<D: DropdownDelegate + 'static> {
cleanable: bool,
placeholder: SharedString,
title_prefix: Option<SharedString>,
selected_value: Option<SharedString>,
selected_value: Option<<D::Item as DropdownItem>::Value>,
}
impl<D> Dropdown<D>
@ -228,6 +251,18 @@ where
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> {
self.list.read(cx).selected_index()
}
@ -236,11 +271,11 @@ where
self.selected_value = self
.selected_index(cx)
.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> {
self.selected_value.clone()
pub fn selected_value(&self) -> Option<&<D::Item as DropdownItem>::Value> {
self.selected_value.as_ref()
}
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(
id: impl Into<ElementId>,
items: Rc<Vec<SharedString>>,
items: Vec<impl Into<SharedString>>,
selected_index: Option<usize>,
cx: &mut ViewContext<Self>,
) -> Self {
let delegate = StringDropdownDelegate {
items: items.clone(),
};
Self::new(id, delegate, selected_index, cx)
let items = items.into_iter().map(Into::into).collect();
Self::new(id, items, selected_index, cx)
}
}