use std::rc::Rc; use gpui::{ AnyElement, App, Context, Div, ElementId, Entity, InteractiveElement, IntoElement, MouseButton, ParentElement, Point, RenderOnce, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::*, }; use crate::{ data_display::color_mark, forms::{ Choice, Down, Enter, OnValue, OnValues, Pick, SearchInput, TextInput, Up, marked_row, reveal, revealer, }, layout::on_axis, primitives::IconName, theme::{ActiveTheme, TextSize}, }; /// A label: its key, its name, and its hue among the theme's chart colors. #[derive(Clone, Debug, PartialEq)] pub struct Label { pub key: SharedString, pub name: SharedString, pub hue: usize, } impl Label { pub fn new(key: impl Into, name: impl Into, hue: usize) -> Self { Self { key: key.into(), name: name.into(), hue, } } } /// Label `name`'s dot in the chart color at `hue`, centered in an icon's box. pub(super) fn hue_dot(name: &str, hue: usize, cx: &App) -> Div { color_mark(cx.theme().colors.hue(hue, format_args!("label {name}")), cx) } /// The search field, the row under the keyboard, the query it was set for, and the list's scroll. struct Finding { search: Entity, at: usize, query: String, revealed: Option, scroll: ScrollHandle, } /// Moves the row under the keyboard by `by` among `count`, wrapping. fn step(finding: &Entity, count: usize, by: isize, cx: &mut App) { if count == 0 { return; } finding.update(cx, |finding, cx| { let at = finding.at.min(count - 1) as isize; finding.at = (at + by).rem_euclid(count as isize) as usize; cx.notify(); }) } /// Labels to put on a message: the field finds them by name, arrows move between them, and a press or Enter turns one on or off. With `on_create`, a name no label has can be made into one. #[derive(IntoElement)] pub struct LabelPicker { id: ElementId, labels: Vec