command 2 2 (#3317)
- Update command matches faster - Fix action dispatching... - Add Text::styled() and use it in command palette - Fix SingleLine editor font size - Fix elevation on go_to_line2 - Allow clicking on commands in the command palette Release Notes: - N/A
This commit is contained in:
commit
58f9ef99f7
11 changed files with 157 additions and 58 deletions
|
@ -130,16 +130,7 @@ impl CommandPaletteDelegate {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
command_palette,
|
command_palette,
|
||||||
matches: commands
|
matches: vec![],
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, command)| StringMatch {
|
|
||||||
candidate_id: i,
|
|
||||||
string: command.name.clone(),
|
|
||||||
positions: Vec::new(),
|
|
||||||
score: 0.0,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
commands,
|
commands,
|
||||||
selected_ix: 0,
|
selected_ix: 0,
|
||||||
previous_focus_handle,
|
previous_focus_handle,
|
||||||
|
|
|
@ -9383,7 +9383,7 @@ impl Render for Editor {
|
||||||
color: cx.theme().colors().text,
|
color: cx.theme().colors().text,
|
||||||
font_family: "Zed Sans".into(), // todo!()
|
font_family: "Zed Sans".into(), // todo!()
|
||||||
font_features: FontFeatures::default(),
|
font_features: FontFeatures::default(),
|
||||||
font_size: rems(1.0).into(),
|
font_size: rems(0.875).into(),
|
||||||
font_weight: FontWeight::NORMAL,
|
font_weight: FontWeight::NORMAL,
|
||||||
font_style: FontStyle::Normal,
|
font_style: FontStyle::Normal,
|
||||||
line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
|
line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
|
||||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use text::{Bias, Point};
|
use text::{Bias, Point};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::{h_stack, modal, v_stack, Label, LabelColor};
|
use ui::{h_stack, v_stack, Label, LabelColor, StyledExt};
|
||||||
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||||
use workspace::{Modal, ModalEvent, Workspace};
|
use workspace::{Modal, ModalEvent, Workspace};
|
||||||
|
|
||||||
|
@ -148,7 +148,8 @@ impl Render for GoToLine {
|
||||||
type Element = Div<Self>;
|
type Element = Div<Self>;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
modal(cx)
|
div()
|
||||||
|
.elevation_2(cx)
|
||||||
.context("GoToLine")
|
.context("GoToLine")
|
||||||
.on_action(Self::cancel)
|
.on_action(Self::cancel)
|
||||||
.on_action(Self::confirm)
|
.on_action(Self::confirm)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, Line, Pixels, SharedString,
|
AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, Line, Pixels, SharedString,
|
||||||
Size, ViewContext,
|
Size, TextRun, ViewContext,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -11,6 +11,7 @@ impl<V: 'static> Component<V> for SharedString {
|
||||||
fn render(self) -> AnyElement<V> {
|
fn render(self) -> AnyElement<V> {
|
||||||
Text {
|
Text {
|
||||||
text: self,
|
text: self,
|
||||||
|
runs: None,
|
||||||
state_type: PhantomData,
|
state_type: PhantomData,
|
||||||
}
|
}
|
||||||
.render()
|
.render()
|
||||||
|
@ -21,6 +22,7 @@ impl<V: 'static> Component<V> for &'static str {
|
||||||
fn render(self) -> AnyElement<V> {
|
fn render(self) -> AnyElement<V> {
|
||||||
Text {
|
Text {
|
||||||
text: self.into(),
|
text: self.into(),
|
||||||
|
runs: None,
|
||||||
state_type: PhantomData,
|
state_type: PhantomData,
|
||||||
}
|
}
|
||||||
.render()
|
.render()
|
||||||
|
@ -33,6 +35,7 @@ impl<V: 'static> Component<V> for String {
|
||||||
fn render(self) -> AnyElement<V> {
|
fn render(self) -> AnyElement<V> {
|
||||||
Text {
|
Text {
|
||||||
text: self.into(),
|
text: self.into(),
|
||||||
|
runs: None,
|
||||||
state_type: PhantomData,
|
state_type: PhantomData,
|
||||||
}
|
}
|
||||||
.render()
|
.render()
|
||||||
|
@ -41,9 +44,25 @@ impl<V: 'static> Component<V> for String {
|
||||||
|
|
||||||
pub struct Text<V> {
|
pub struct Text<V> {
|
||||||
text: SharedString,
|
text: SharedString,
|
||||||
|
runs: Option<Vec<TextRun>>,
|
||||||
state_type: PhantomData<V>,
|
state_type: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> Text<V> {
|
||||||
|
/// styled renders text that has different runs of different styles.
|
||||||
|
/// callers are responsible for setting the correct style for each run.
|
||||||
|
////
|
||||||
|
/// For uniform text you can usually just pass a string as a child, and
|
||||||
|
/// cx.text_style() will be used automatically.
|
||||||
|
pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
|
||||||
|
Text {
|
||||||
|
text,
|
||||||
|
runs: Some(runs),
|
||||||
|
state_type: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<V: 'static> Component<V> for Text<V> {
|
impl<V: 'static> Component<V> for Text<V> {
|
||||||
fn render(self) -> AnyElement<V> {
|
fn render(self) -> AnyElement<V> {
|
||||||
AnyElement::new(self)
|
AnyElement::new(self)
|
||||||
|
@ -82,6 +101,12 @@ impl<V: 'static> Element<V> for Text<V> {
|
||||||
|
|
||||||
let rem_size = cx.rem_size();
|
let rem_size = cx.rem_size();
|
||||||
|
|
||||||
|
let runs = if let Some(runs) = self.runs.take() {
|
||||||
|
runs
|
||||||
|
} else {
|
||||||
|
vec![text_style.to_run(text.len())]
|
||||||
|
};
|
||||||
|
|
||||||
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
||||||
let element_state = element_state.clone();
|
let element_state = element_state.clone();
|
||||||
move |known_dimensions, _| {
|
move |known_dimensions, _| {
|
||||||
|
@ -89,7 +114,7 @@ impl<V: 'static> Element<V> for Text<V> {
|
||||||
.layout_text(
|
.layout_text(
|
||||||
&text,
|
&text,
|
||||||
font_size,
|
font_size,
|
||||||
&[text_style.to_run(text.len())],
|
&runs[..],
|
||||||
known_dimensions.width, // Wrap if we know the width.
|
known_dimensions.width, // Wrap if we know the width.
|
||||||
)
|
)
|
||||||
.log_err()
|
.log_err()
|
||||||
|
|
|
@ -71,6 +71,40 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_any_mouse_down(
|
||||||
|
mut self,
|
||||||
|
handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.stateless_interactivity()
|
||||||
|
.mouse_down_listeners
|
||||||
|
.push(Box::new(move |view, event, bounds, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
|
||||||
|
handler(view, event, cx)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_any_mouse_up(
|
||||||
|
mut self,
|
||||||
|
handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.stateless_interactivity()
|
||||||
|
.mouse_up_listeners
|
||||||
|
.push(Box::new(move |view, event, bounds, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
|
||||||
|
handler(view, event, cx)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn on_mouse_up(
|
fn on_mouse_up(
|
||||||
mut self,
|
mut self,
|
||||||
button: MouseButton,
|
button: MouseButton,
|
||||||
|
@ -111,7 +145,6 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
|
||||||
|
|
||||||
fn on_mouse_up_out(
|
fn on_mouse_up_out(
|
||||||
mut self,
|
mut self,
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
|
handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
|
@ -120,10 +153,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
|
||||||
self.stateless_interactivity()
|
self.stateless_interactivity()
|
||||||
.mouse_up_listeners
|
.mouse_up_listeners
|
||||||
.push(Box::new(move |view, event, bounds, phase, cx| {
|
.push(Box::new(move |view, event, bounds, phase, cx| {
|
||||||
if phase == DispatchPhase::Capture
|
if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
|
||||||
&& event.button == button
|
|
||||||
&& !bounds.contains_point(&event.position)
|
|
||||||
{
|
|
||||||
handler(view, event, cx);
|
handler(view, event, cx);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -368,6 +368,7 @@ impl Display for FontStyle {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct TextRun {
|
pub struct TextRun {
|
||||||
|
// number of utf8 bytes
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub font: Font,
|
pub font: Font,
|
||||||
pub color: Hsla,
|
pub color: Hsla,
|
||||||
|
|
|
@ -101,6 +101,12 @@ pub struct FocusHandle {
|
||||||
handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for FocusHandle {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!("FocusHandle({:?})", self.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FocusHandle {
|
impl FocusHandle {
|
||||||
pub(crate) fn new(handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>) -> Self {
|
pub(crate) fn new(handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>) -> Self {
|
||||||
let id = handles.write().insert(AtomicUsize::new(1));
|
let id = handles.write().insert(AtomicUsize::new(1));
|
||||||
|
@ -424,6 +430,7 @@ impl<'a> WindowContext<'a> {
|
||||||
.dispatch_tree
|
.dispatch_tree
|
||||||
.focusable_node_id(focus_handle.id)
|
.focusable_node_id(focus_handle.id)
|
||||||
{
|
{
|
||||||
|
cx.propagate_event = true;
|
||||||
cx.dispatch_action_on_node(node_id, action);
|
cx.dispatch_action_on_node(node_id, action);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, uniform_list, Component, Div, ParentElement, Render, StatelessInteractive, Styled, Task,
|
div, uniform_list, Component, Div, MouseButton, ParentElement, Render, StatelessInteractive,
|
||||||
UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
|
Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
use std::{cmp, sync::Arc};
|
use std::{cmp, sync::Arc};
|
||||||
use ui::{prelude::*, v_stack, Divider, Label, LabelColor};
|
use ui::{prelude::*, v_stack, Divider, Label, LabelColor};
|
||||||
|
@ -10,7 +10,8 @@ pub struct Picker<D: PickerDelegate> {
|
||||||
pub delegate: D,
|
pub delegate: D,
|
||||||
scroll_handle: UniformListScrollHandle,
|
scroll_handle: UniformListScrollHandle,
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
pending_update_matches: Option<Task<Option<()>>>,
|
pending_update_matches: Option<Task<()>>,
|
||||||
|
confirm_on_update: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PickerDelegate: Sized + 'static {
|
pub trait PickerDelegate: Sized + 'static {
|
||||||
|
@ -42,12 +43,15 @@ impl<D: PickerDelegate> Picker<D> {
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
cx.subscribe(&editor, Self::on_input_editor_event).detach();
|
cx.subscribe(&editor, Self::on_input_editor_event).detach();
|
||||||
Self {
|
let mut this = Self {
|
||||||
delegate,
|
delegate,
|
||||||
|
editor,
|
||||||
scroll_handle: UniformListScrollHandle::new(),
|
scroll_handle: UniformListScrollHandle::new(),
|
||||||
pending_update_matches: None,
|
pending_update_matches: None,
|
||||||
editor,
|
confirm_on_update: None,
|
||||||
}
|
};
|
||||||
|
this.update_matches("".to_string(), cx);
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(&self, cx: &mut WindowContext) {
|
pub fn focus(&self, cx: &mut WindowContext) {
|
||||||
|
@ -99,11 +103,26 @@ impl<D: PickerDelegate> Picker<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
||||||
self.delegate.confirm(false, cx);
|
if self.pending_update_matches.is_some() {
|
||||||
|
self.confirm_on_update = Some(false)
|
||||||
|
} else {
|
||||||
|
self.delegate.confirm(false, cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
|
fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
|
||||||
self.delegate.confirm(true, cx);
|
if self.pending_update_matches.is_some() {
|
||||||
|
self.confirm_on_update = Some(true)
|
||||||
|
} else {
|
||||||
|
self.delegate.confirm(true, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_click(&mut self, ix: usize, secondary: bool, cx: &mut ViewContext<Self>) {
|
||||||
|
cx.stop_propagation();
|
||||||
|
cx.prevent_default();
|
||||||
|
self.delegate.set_selected_index(ix, cx);
|
||||||
|
self.delegate.confirm(secondary, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_input_editor_event(
|
fn on_input_editor_event(
|
||||||
|
@ -126,7 +145,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.matches_updated(cx);
|
this.matches_updated(cx);
|
||||||
})
|
})
|
||||||
.ok()
|
.ok();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +153,9 @@ impl<D: PickerDelegate> Picker<D> {
|
||||||
let index = self.delegate.selected_index();
|
let index = self.delegate.selected_index();
|
||||||
self.scroll_handle.scroll_to_item(index);
|
self.scroll_handle.scroll_to_item(index);
|
||||||
self.pending_update_matches = None;
|
self.pending_update_matches = None;
|
||||||
|
if let Some(secondary) = self.confirm_on_update.take() {
|
||||||
|
self.delegate.confirm(secondary, cx);
|
||||||
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +193,22 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
||||||
let selected_ix = this.delegate.selected_index();
|
let selected_ix = this.delegate.selected_index();
|
||||||
visible_range
|
visible_range
|
||||||
.map(|ix| {
|
.map(|ix| {
|
||||||
this.delegate.render_match(ix, ix == selected_ix, cx)
|
div()
|
||||||
|
.on_mouse_down(
|
||||||
|
MouseButton::Left,
|
||||||
|
move |this: &mut Self, event, cx| {
|
||||||
|
this.handle_click(
|
||||||
|
ix,
|
||||||
|
event.modifiers.command,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.child(this.delegate.render_match(
|
||||||
|
ix,
|
||||||
|
ix == selected_ix,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -184,10 +221,11 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
||||||
})
|
})
|
||||||
.when(self.delegate.match_count() == 0, |el| {
|
.when(self.delegate.match_count() == 0, |el| {
|
||||||
el.child(
|
el.child(
|
||||||
v_stack()
|
v_stack().p_1().grow().child(
|
||||||
.p_1()
|
div()
|
||||||
.grow()
|
.px_1()
|
||||||
.child(Label::new("No matches").color(LabelColor::Muted)),
|
.child(Label::new("No matches").color(LabelColor::Muted)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ impl KeyBinding {
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
|
.when(keystroke.modifiers.function, |el| el.child(Key::new("fn")))
|
||||||
.when(keystroke.modifiers.control, |el| el.child(Key::new("^")))
|
.when(keystroke.modifiers.control, |el| el.child(Key::new("^")))
|
||||||
.when(keystroke.modifiers.alt, |el| el.child(Key::new("⌥")))
|
.when(keystroke.modifiers.alt, |el| el.child(Key::new("⌥")))
|
||||||
.when(keystroke.modifiers.command, |el| el.child(Key::new("⌘")))
|
.when(keystroke.modifiers.command, |el| el.child(Key::new("⌘")))
|
||||||
|
@ -136,6 +137,7 @@ mod stories {
|
||||||
.child(KeyBinding::new(binding("a z")))
|
.child(KeyBinding::new(binding("a z")))
|
||||||
.child(Story::label(cx, "Chord with Modifier"))
|
.child(Story::label(cx, "Chord with Modifier"))
|
||||||
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
||||||
|
.child(KeyBinding::new(binding("fn-s")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use gpui::{relative, Hsla, WindowContext};
|
use gpui::{relative, Hsla, Text, TextRun, WindowContext};
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::styled_ext::StyledExt;
|
use crate::styled_ext::StyledExt;
|
||||||
|
@ -105,6 +104,8 @@ pub struct HighlightedLabel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HighlightedLabel {
|
impl HighlightedLabel {
|
||||||
|
/// shows a label with the given characters highlighted.
|
||||||
|
/// characters are identified by utf8 byte position.
|
||||||
pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
|
pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
|
@ -126,10 +127,11 @@ impl HighlightedLabel {
|
||||||
|
|
||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
let highlight_color = cx.theme().colors().text_accent;
|
let highlight_color = cx.theme().colors().text_accent;
|
||||||
|
let mut text_style = cx.text_style().clone();
|
||||||
|
|
||||||
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
||||||
|
|
||||||
let mut runs: SmallVec<[Run; 8]> = SmallVec::new();
|
let mut runs: Vec<TextRun> = Vec::new();
|
||||||
|
|
||||||
for (char_ix, char) in self.label.char_indices() {
|
for (char_ix, char) in self.label.char_indices() {
|
||||||
let mut color = self.color.hsla(cx);
|
let mut color = self.color.hsla(cx);
|
||||||
|
@ -137,16 +139,14 @@ impl HighlightedLabel {
|
||||||
if let Some(highlight_ix) = highlight_indices.peek() {
|
if let Some(highlight_ix) = highlight_indices.peek() {
|
||||||
if char_ix == *highlight_ix {
|
if char_ix == *highlight_ix {
|
||||||
color = highlight_color;
|
color = highlight_color;
|
||||||
|
|
||||||
highlight_indices.next();
|
highlight_indices.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_run = runs.last_mut();
|
let last_run = runs.last_mut();
|
||||||
|
|
||||||
let start_new_run = if let Some(last_run) = last_run {
|
let start_new_run = if let Some(last_run) = last_run {
|
||||||
if color == last_run.color {
|
if color == last_run.color {
|
||||||
last_run.text.push(char);
|
last_run.len += char.len_utf8();
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -156,10 +156,8 @@ impl HighlightedLabel {
|
||||||
};
|
};
|
||||||
|
|
||||||
if start_new_run {
|
if start_new_run {
|
||||||
runs.push(Run {
|
text_style.color = color;
|
||||||
text: char.to_string(),
|
runs.push(text_style.to_run(char.len_utf8()))
|
||||||
color,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,10 +174,7 @@ impl HighlightedLabel {
|
||||||
.bg(LabelColor::Hidden.hsla(cx)),
|
.bg(LabelColor::Hidden.hsla(cx)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.children(
|
.child(Text::styled(self.label, runs))
|
||||||
runs.into_iter()
|
|
||||||
.map(|run| div().text_color(run.color).child(run.text)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +208,10 @@ mod stories {
|
||||||
"Hello, world!",
|
"Hello, world!",
|
||||||
vec![0, 1, 2, 7, 8, 12],
|
vec![0, 1, 2, 7, 8, 12],
|
||||||
))
|
))
|
||||||
|
.child(HighlightedLabel::new(
|
||||||
|
"Héllo, world!",
|
||||||
|
vec![0, 1, 3, 8, 9, 13],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use gpui::{
|
||||||
div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
|
div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
|
||||||
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
|
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
use ui::v_stack;
|
use ui::{h_stack, v_stack};
|
||||||
|
|
||||||
pub struct ActiveModal {
|
pub struct ActiveModal {
|
||||||
modal: AnyView,
|
modal: AnyView,
|
||||||
|
@ -33,8 +33,6 @@ impl ModalLayer {
|
||||||
V: Modal,
|
V: Modal,
|
||||||
B: FnOnce(&mut ViewContext<V>) -> V,
|
B: FnOnce(&mut ViewContext<V>) -> V,
|
||||||
{
|
{
|
||||||
let previous_focus = cx.focused();
|
|
||||||
|
|
||||||
if let Some(active_modal) = &self.active_modal {
|
if let Some(active_modal) = &self.active_modal {
|
||||||
let is_close = active_modal.modal.clone().downcast::<V>().is_ok();
|
let is_close = active_modal.modal.clone().downcast::<V>().is_ok();
|
||||||
self.hide_modal(cx);
|
self.hide_modal(cx);
|
||||||
|
@ -85,9 +83,6 @@ impl Render for ModalLayer {
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.absolute()
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.items_center()
|
|
||||||
.size_full()
|
.size_full()
|
||||||
.top_0()
|
.top_0()
|
||||||
.left_0()
|
.left_0()
|
||||||
|
@ -96,11 +91,21 @@ impl Render for ModalLayer {
|
||||||
v_stack()
|
v_stack()
|
||||||
.h(px(0.0))
|
.h(px(0.0))
|
||||||
.top_20()
|
.top_20()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.items_center()
|
||||||
.track_focus(&active_modal.focus_handle)
|
.track_focus(&active_modal.focus_handle)
|
||||||
.on_mouse_down_out(|this: &mut Self, event, cx| {
|
.child(
|
||||||
this.hide_modal(cx);
|
h_stack()
|
||||||
})
|
// needed to prevent mouse events leaking to the
|
||||||
.child(active_modal.modal.clone()),
|
// UI below. // todo! for gpui3.
|
||||||
|
.on_any_mouse_down(|_, _, cx| cx.stop_propagation())
|
||||||
|
.on_any_mouse_up(|_, _, cx| cx.stop_propagation())
|
||||||
|
.on_mouse_down_out(|this: &mut Self, event, cx| {
|
||||||
|
this.hide_modal(cx);
|
||||||
|
})
|
||||||
|
.child(active_modal.modal.clone()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue