Cargo.lock 🔗
@@ -11351,6 +11351,7 @@ dependencies = [
"libc",
"log",
"lsp2",
+ "menu2",
"node_runtime",
"num_cpus",
"parking_lot 0.11.2",
Mikayla created
Cargo.lock | 1
crates/editor2/src/display_map.rs | 16
crates/editor2/src/editor.rs | 106 ++++-
crates/editor2/src/element.rs | 22
crates/go_to_line2/src/go_to_line.rs | 383 +++++++++-----------
crates/gpui2/src/action.rs | 1
crates/gpui2/src/app.rs | 25 +
crates/gpui2/src/app/async_context.rs | 2
crates/gpui2/src/element.rs | 25 +
crates/gpui2/src/gpui2.rs | 2
crates/gpui2/src/platform.rs | 2
crates/gpui2/src/platform/mac/window.rs | 8
crates/gpui2/src/styled.rs | 19
crates/gpui2/src/window.rs | 45 +-
crates/gpui2/src/window_input_handler.rs | 186 +++++++--
crates/menu2/src/menu2.rs | 5
crates/theme2/src/theme2.rs | 2
crates/ui2/src/components.rs | 2
crates/ui2/src/components/elevated_surface.rs | 28 +
crates/ui2/src/elevation.rs | 27 +
crates/workspace2/src/dock.rs | 8
crates/workspace2/src/modal_layer.rs | 101 +++--
crates/workspace2/src/toolbar.rs | 67 ---
crates/workspace2/src/workspace2.rs | 19
crates/zed2/Cargo.toml | 1
crates/zed2/src/main.rs | 4
styles/src/style_tree/assistant.ts | 114 +++++-
styles/src/style_tree/status_bar.ts | 18
styles/src/themes/rose-pine/rose-pine-dawn.ts | 2
test.rs | 24
30 files changed, 755 insertions(+), 510 deletions(-)
@@ -11351,6 +11351,7 @@ dependencies = [
"libc",
"log",
"lsp2",
+ "menu2",
"node_runtime",
"num_cpus",
"parking_lot 0.11.2",
@@ -572,7 +572,6 @@ impl DisplaySnapshot {
) -> Line {
let mut runs = Vec::new();
let mut line = String::new();
- let mut ended_in_newline = false;
let range = display_row..display_row + 1;
for chunk in self.highlighted_chunks(range, false, &editor_style) {
@@ -588,17 +587,18 @@ impl DisplaySnapshot {
} else {
Cow::Borrowed(&editor_style.text)
};
- ended_in_newline = chunk.chunk.ends_with("\n");
runs.push(text_style.to_run(chunk.chunk.len()))
}
- // our pixel positioning logic assumes each line ends in \n,
- // this is almost always true except for the last line which
- // may have no trailing newline.
- if !ended_in_newline && display_row == self.max_point().row() {
- line.push_str("\n");
- runs.push(editor_style.text.to_run("\n".len()));
+ if line.ends_with('\n') {
+ line.pop();
+ if let Some(last_run) = runs.last_mut() {
+ last_run.len -= 1;
+ if last_run.len == 0 {
+ runs.pop();
+ }
+ }
}
let font_size = editor_style.text.font_size.to_pixels(*rem_size);
@@ -39,11 +39,10 @@ use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
use gpui::{
- action, actions, div, px, relative, AnyElement, AppContext, BackgroundExecutor, BorrowWindow,
- ClipboardItem, Context, DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle,
- FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, PlatformInputHandler,
- Render, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView,
- WindowContext,
+ action, actions, point, px, relative, rems, size, AnyElement, AppContext, BackgroundExecutor,
+ Bounds, ClipboardItem, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures,
+ FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, Subscription,
+ Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -57,6 +56,7 @@ use language::{
Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName,
OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
};
+use lazy_static::lazy_static;
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
use lsp::{DiagnosticSeverity, Documentation, LanguageServerId};
use movement::TextLayoutDetails;
@@ -66,7 +66,7 @@ pub use multi_buffer::{
ToPoint,
};
use ordered_float::OrderedFloat;
-use parking_lot::RwLock;
+use parking_lot::{Mutex, RwLock};
use project::{FormatTrigger, Location, Project};
use rand::prelude::*;
use rpc::proto::*;
@@ -676,6 +676,7 @@ pub struct Editor {
next_inlay_id: usize,
_subscriptions: Vec<Subscription>,
pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
+ gutter_width: Pixels,
style: Option<EditorStyle>,
}
@@ -1987,6 +1988,7 @@ impl Editor {
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false,
pixel_position_of_newest_cursor: None,
+ gutter_width: Default::default(),
style: None,
_subscriptions: vec![
cx.observe(&buffer, Self::on_buffer_changed),
@@ -2181,14 +2183,14 @@ impl Editor {
// self.collaboration_hub = Some(hub);
// }
- // pub fn set_placeholder_text(
- // &mut self,
- // placeholder_text: impl Into<Arc<str>>,
- // cx: &mut ViewContext<Self>,
- // ) {
- // self.placeholder_text = Some(placeholder_text.into());
- // cx.notify();
- // }
+ pub fn set_placeholder_text(
+ &mut self,
+ placeholder_text: impl Into<Arc<str>>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ self.placeholder_text = Some(placeholder_text.into());
+ cx.notify();
+ }
// pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
// self.cursor_shape = cursor_shape;
@@ -9431,18 +9433,42 @@ impl Render for Editor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let settings = ThemeSettings::get_global(cx);
- let text_style = TextStyle {
- color: cx.theme().colors().text,
- font_family: settings.buffer_font.family.clone(),
- font_features: settings.buffer_font.features,
- font_size: settings.buffer_font_size.into(),
- font_weight: FontWeight::NORMAL,
- font_style: FontStyle::Normal,
- line_height: relative(settings.buffer_line_height.value()),
- underline: None,
+ let text_style = match self.mode {
+ EditorMode::SingleLine => {
+ TextStyle {
+ color: cx.theme().colors().text,
+ font_family: "Zed Sans".into(), // todo!()
+ font_features: FontFeatures::default(),
+ font_size: rems(1.0).into(),
+ font_weight: FontWeight::NORMAL,
+ font_style: FontStyle::Normal,
+ line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
+ underline: None,
+ }
+ }
+
+ EditorMode::AutoHeight { max_lines } => todo!(),
+
+ EditorMode::Full => TextStyle {
+ color: cx.theme().colors().text,
+ font_family: settings.buffer_font.family.clone(),
+ font_features: settings.buffer_font.features,
+ font_size: settings.buffer_font_size.into(),
+ font_weight: FontWeight::NORMAL,
+ font_style: FontStyle::Normal,
+ line_height: relative(settings.buffer_line_height.value()),
+ underline: None,
+ },
};
+
+ let background = match self.mode {
+ EditorMode::SingleLine => cx.theme().system().transparent,
+ EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
+ EditorMode::Full => cx.theme().colors().editor_background,
+ };
+
EditorElement::new(EditorStyle {
- background: cx.theme().colors().editor_background,
+ background,
local_player: cx.theme().players().local(),
text: text_style,
scrollbar_width: px(12.),
@@ -9765,13 +9791,35 @@ impl InputHandler for Editor {
}
fn bounds_for_range(
- &self,
+ &mut self,
range_utf16: Range<usize>,
+ element_bounds: gpui::Bounds<Pixels>,
cx: &mut ViewContext<Self>,
- ) -> Option<gpui::Bounds<f32>> {
- // todo!()
- // See how we did it before: `rect_for_range`
- None
+ ) -> Option<gpui::Bounds<Pixels>> {
+ let text_layout_details = self.text_layout_details(cx);
+ let style = &text_layout_details.editor_style;
+ let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
+ let font_size = style.text.font_size.to_pixels(cx.rem_size());
+ let line_height = style.text.line_height_in_pixels(cx.rem_size());
+ let em_width = cx
+ .text_system()
+ .typographic_bounds(font_id, font_size, 'm')
+ .unwrap()
+ .size
+ .width;
+
+ let snapshot = self.snapshot(cx);
+ let scroll_position = snapshot.scroll_position();
+ let scroll_left = scroll_position.x * em_width;
+
+ let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
+ let x = snapshot.x_for_point(start, &text_layout_details) - scroll_left + self.gutter_width;
+ let y = line_height * (start.row() as f32 - scroll_position.y);
+
+ Some(Bounds {
+ origin: element_bounds.origin + point(x, y),
+ size: size(em_width, line_height),
+ })
}
}
@@ -17,10 +17,11 @@ use collections::{BTreeMap, HashMap};
use gpui::{
black, hsla, point, px, relative, size, transparent_black, Action, AnyElement,
BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchContext, DispatchPhase,
- Edges, Element, ElementId, Entity, GlobalElementId, Hsla, KeyDownEvent, KeyListener, KeyMatch,
- Line, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
- ScrollWheelEvent, ShapedGlyph, Size, Style, TextRun, TextStyle, TextSystem, ViewContext,
- WindowContext,
+ Edges, Element, ElementId, Entity, FocusHandle, GlobalElementId, Hsla, InputHandler,
+ InputHandlerView, KeyDownEvent, KeyListener, KeyMatch, Line, LineLayout, Modifiers,
+ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent,
+ ShapedGlyph, Size, Style, TextRun, TextStyle, TextSystem, ViewContext, WindowContext,
+ WrappedLineLayout,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -1467,6 +1468,7 @@ impl EditorElement {
gutter_margin = Pixels::ZERO;
};
+ editor.gutter_width = gutter_width;
let text_width = bounds.size.width - gutter_width;
let overscroll = size(em_width, px(0.));
let snapshot = {
@@ -2502,10 +2504,6 @@ impl Element<Editor> for EditorElement {
size: layout.text_size,
};
- if editor.focus_handle.is_focused(cx) {
- cx.handle_text_input();
- }
-
cx.with_content_mask(ContentMask { bounds }, |cx| {
self.paint_mouse_listeners(
bounds,
@@ -2521,6 +2519,14 @@ impl Element<Editor> for EditorElement {
self.paint_text(text_bounds, &layout, editor, cx);
});
}
+
+ fn handle_text_input<'a>(
+ &self,
+ editor: &'a mut Editor,
+ cx: &mut ViewContext<Editor>,
+ ) -> Option<(Box<dyn InputHandlerView>, &'a FocusHandle)> {
+ Some((Box::new(cx.view()), &editor.focus_handle))
+ }
}
// impl EditorElement {
@@ -1,220 +1,187 @@
-use gpui::{actions, div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext};
-use workspace::ModalRegistry;
+use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
+use gpui::{
+ actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString,
+ StatefulInteractivity, StatelessInteractive, Styled, Subscription, View, ViewContext,
+ VisualContext, WindowContext,
+};
+use text::{Bias, Point};
+use theme::ActiveTheme;
+use ui::{h_stack, modal, v_stack, Label, LabelColor};
+use util::paths::FILE_ROW_COLUMN_DELIMITER;
+use workspace::{ModalEvent, Workspace};
actions!(Toggle);
pub fn init(cx: &mut AppContext) {
- cx.global_mut::<ModalRegistry>()
- .register_modal(Toggle, |_, cx| {
- // if let Some(editor) = workspace
- // .active_item(cx)
- // .and_then(|active_item| active_item.downcast::<Editor>())
- // {
- // cx.build_view(|cx| GoToLine::new(editor, cx))
- // }
- let view = cx.build_view(|_| GoToLine);
- view
- });
+ cx.observe_new_views(
+ |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
+ workspace
+ .modal_layer()
+ .register_modal(Toggle, |workspace, cx| {
+ let editor = workspace
+ .active_item(cx)
+ .and_then(|active_item| active_item.downcast::<Editor>())?;
+
+ Some(cx.build_view(|cx| GoToLine::new(editor, cx)))
+ });
+ },
+ )
+ .detach();
+}
+
+pub struct GoToLine {
+ line_editor: View<Editor>,
+ active_editor: View<Editor>,
+ current_text: SharedString,
+ prev_scroll_position: Option<gpui::Point<f32>>,
+ _subscriptions: Vec<Subscription>,
+}
- // cx.add_action(GoToLine::toggle);
- // cx.add_action(GoToLine::confirm);
- // cx.add_action(GoToLine::cancel);
+pub enum Event {
+ Dismissed,
}
-pub struct GoToLine;
+impl EventEmitter<Event> for GoToLine {}
-impl Render for GoToLine {
- type Element = Div<Self>;
+impl EventEmitter<ModalEvent> for GoToLine {}
- fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
- div().bg(red()).w(px(100.0)).h(px(100.0))
+impl GoToLine {
+ pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
+ let line_editor = cx.build_view(|cx| {
+ let editor = Editor::single_line(cx);
+ editor.focus(cx);
+ editor
+ });
+ let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);
+
+ let editor = active_editor.read(cx);
+ let cursor = editor.selections.last::<Point>(cx).head();
+ let last_line = editor.buffer().read(cx).snapshot(cx).max_point().row;
+ let scroll_position = active_editor.update(cx, |editor, cx| editor.scroll_position(cx));
+
+ let current_text = format!(
+ "line {} of {} (column {})",
+ cursor.row + 1,
+ last_line + 1,
+ cursor.column + 1,
+ );
+
+ Self {
+ line_editor,
+ active_editor,
+ current_text: current_text.into(),
+ prev_scroll_position: Some(scroll_position),
+ _subscriptions: vec![line_editor_change, cx.on_release(Self::release)],
+ }
+ }
+
+ fn release(&mut self, cx: &mut WindowContext) {
+ let scroll_position = self.prev_scroll_position.take();
+ self.active_editor.update(cx, |editor, cx| {
+ editor.focus(cx);
+ editor.highlight_rows(None);
+ if let Some(scroll_position) = scroll_position {
+ editor.set_scroll_position(scroll_position, cx);
+ }
+ cx.notify();
+ })
+ }
+
+ fn on_line_editor_event(
+ &mut self,
+ _: View<Editor>,
+ event: &editor::Event,
+ cx: &mut ViewContext<Self>,
+ ) {
+ match event {
+ // todo!() this isn't working...
+ editor::Event::Blurred => cx.emit(Event::Dismissed),
+ editor::Event::BufferEdited { .. } => self.highlight_current_line(cx),
+ _ => {}
+ }
+ }
+
+ fn highlight_current_line(&mut self, cx: &mut ViewContext<Self>) {
+ if let Some(point) = self.point_from_query(cx) {
+ self.active_editor.update(cx, |active_editor, cx| {
+ let snapshot = active_editor.snapshot(cx).display_snapshot;
+ let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
+ let display_point = point.to_display_point(&snapshot);
+ let row = display_point.row();
+ active_editor.highlight_rows(Some(row..row + 1));
+ active_editor.request_autoscroll(Autoscroll::center(), cx);
+ });
+ cx.notify();
+ }
+ }
+
+ fn point_from_query(&self, cx: &ViewContext<Self>) -> Option<Point> {
+ let line_editor = self.line_editor.read(cx).text(cx);
+ let mut components = line_editor
+ .splitn(2, FILE_ROW_COLUMN_DELIMITER)
+ .map(str::trim)
+ .fuse();
+ let row = components.next().and_then(|row| row.parse::<u32>().ok())?;
+ let column = components.next().and_then(|col| col.parse::<u32>().ok());
+ Some(Point::new(
+ row.saturating_sub(1),
+ column.unwrap_or(0).saturating_sub(1),
+ ))
+ }
+
+ fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+ cx.emit(Event::Dismissed);
+ }
+
+ fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+ if let Some(point) = self.point_from_query(cx) {
+ self.active_editor.update(cx, |active_editor, cx| {
+ let snapshot = active_editor.snapshot(cx).display_snapshot;
+ let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
+ active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+ s.select_ranges([point..point])
+ });
+ });
+ self.prev_scroll_position.take();
+ }
+
+ cx.emit(Event::Dismissed);
}
}
-// pub struct GoToLine {
-// //line_editor: View<Editor>,
-// active_editor: View<Editor>,
-// prev_scroll_position: Option<gpui::Point<Pixels>>,
-// cursor_point: Point,
-// max_point: Point,
-// has_focus: bool,
-// }
-
-// pub enum Event {
-// Dismissed,
-// }
-
-// impl GoToLine {
-// pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
-// // let line_editor = cx.build_view(|cx| {
-// // Editor::single_line(
-// // Some(Arc::new(|theme| theme.picker.input_editor.clone())),
-// // cx,
-// // )
-// // });
-// // cx.subscribe(&line_editor, Self::on_line_editor_event)
-// // .detach();
-
-// let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
-// let scroll_position = editor.scroll_position(cx);
-// let buffer = editor.buffer().read(cx).snapshot(cx);
-// (
-// Some(scroll_position),
-// editor.selections.newest(cx).head(),
-// buffer.max_point(),
-// )
-// });
-
-// cx.on_release(|_, on_release| {}).detach();
-
-// Self {
-// //line_editor,
-// active_editor,
-// prev_scroll_position: scroll_position,
-// cursor_point,
-// max_point,
-// has_focus: false,
-// }
-// }
-
-// fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
-// cx.emit(Event::Dismissed);
-// }
-
-// fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
-// self.prev_scroll_position.take();
-// if let Some(point) = self.point_from_query(cx) {
-// self.active_editor.update(cx, |active_editor, cx| {
-// let snapshot = active_editor.snapshot(cx).display_snapshot;
-// let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
-// active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-// s.select_ranges([point..point])
-// });
-// });
-// }
-
-// cx.emit(Event::Dismissed);
-// }
-
-// fn on_line_editor_event(
-// &mut self,
-// _: View<Editor>,
-// event: &editor::Event,
-// cx: &mut ViewContext<Self>,
-// ) {
-// match event {
-// editor::Event::Blurred => cx.emit(Event::Dismissed),
-// editor::Event::BufferEdited { .. } => {
-// if let Some(point) = self.point_from_query(cx) {
-// // todo!()
-// // self.active_editor.update(cx, |active_editor, cx| {
-// // let snapshot = active_editor.snapshot(cx).display_snapshot;
-// // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
-// // let display_point = point.to_display_point(&snapshot);
-// // let row = display_point.row();
-// // active_editor.highlight_rows(Some(row..row + 1));
-// // active_editor.request_autoscroll(Autoscroll::center(), cx);
-// // });
-// cx.notify();
-// }
-// }
-// _ => {}
-// }
-// }
-
-// fn point_from_query(&self, cx: &ViewContext<Self>) -> Option<Point> {
-// return None;
-// // todo!()
-// // let line_editor = self.line_editor.read(cx).text(cx);
-// // let mut components = line_editor
-// // .splitn(2, FILE_ROW_COLUMN_DELIMITER)
-// // .map(str::trim)
-// // .fuse();
-// // let row = components.next().and_then(|row| row.parse::<u32>().ok())?;
-// // let column = components.next().and_then(|col| col.parse::<u32>().ok());
-// // Some(Point::new(
-// // row.saturating_sub(1),
-// // column.unwrap_or(0).saturating_sub(1),
-// // ))
-// }
-// }
-
-// impl EventEmitter for GoToLine {
-// type Event = Event;
-// }
-
-// impl Entity for GoToLine {
-// fn release(&mut self, cx: &mut AppContext) {
-// let scroll_position = self.prev_scroll_position.take();
-// self.active_editor.window().update(cx, |cx| {
-// self.active_editor.update(cx, |editor, cx| {
-// editor.highlight_rows(None);
-// if let Some(scroll_position) = scroll_position {
-// editor.set_scroll_position(scroll_position, cx);
-// }
-// })
-// });
-// }
-// }
-
-// impl Render for GoToLine {
-// type Element = Div<Self>;
-
-// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
-// // todo!()
-// div()
-// }
-// }
-
-// impl View for GoToLine {
-// fn ui_name() -> &'static str {
-// "GoToLine"
-// }
-
-// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-// let theme = &theme::current(cx).picker;
-
-// let label = format!(
-// "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines",
-// self.cursor_point.row + 1,
-// self.cursor_point.column + 1,
-// self.max_point.row + 1
-// );
-
-// Flex::new(Axis::Vertical)
-// .with_child(
-// ChildView::new(&self.line_editor, cx)
-// .contained()
-// .with_style(theme.input_editor.container),
-// )
-// .with_child(
-// Label::new(label, theme.no_matches.label.clone())
-// .contained()
-// .with_style(theme.no_matches.container),
-// )
-// .contained()
-// .with_style(theme.container)
-// .constrained()
-// .with_max_width(500.0)
-// .into_any_named("go to line")
-// }
-
-// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
-// self.has_focus = true;
-// cx.focus(&self.line_editor);
-// }
-
-// fn focus_out(&mut self, _: AnyView, _: &mut ViewContext<Self>) {
-// self.has_focus = false;
-// }
-// }
-
-// impl Modal for GoToLine {
-// fn has_focus(&self) -> bool {
-// self.has_focus
-// }
-
-// fn dismiss_on_event(event: &Self::Event) -> bool {
-// matches!(event, Event::Dismissed)
-// }
-// }
+impl Render for GoToLine {
+ type Element = Div<Self, StatefulInteractivity<Self>>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ modal(cx)
+ .id("go to line")
+ .on_action(Self::cancel)
+ .on_action(Self::confirm)
+ .w_96()
+ .child(
+ v_stack()
+ .px_1()
+ .pt_0p5()
+ .gap_px()
+ .child(
+ v_stack()
+ .py_0p5()
+ .px_1()
+ .child(div().px_1().py_0p5().child(self.line_editor.clone())),
+ )
+ .child(
+ div()
+ .h_px()
+ .w_full()
+ .bg(cx.theme().colors().element_background),
+ )
+ .child(
+ h_stack()
+ .justify_between()
+ .px_2()
+ .py_1()
+ .child(Label::new(self.current_text.clone()).color(LabelColor::Muted)),
+ ),
+ )
+ }
+}
@@ -123,6 +123,7 @@ pub fn register_action<A: Action>() {
/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
let lock = ACTION_REGISTRY.read();
+
let build_action = lock
.builders_by_name
.get(name)
@@ -18,8 +18,8 @@ use crate::{
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, EventEmitter, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap,
LayoutId, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet,
- Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
- WindowContext, WindowHandle, WindowId,
+ Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext,
+ Window, WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@@ -167,6 +167,7 @@ type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
+type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
// struct FrameConsumer {
// next_frame_callbacks: Vec<FrameCallback>,
@@ -193,6 +194,7 @@ pub struct AppContext {
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
pub(crate) globals_by_type: HashMap<TypeId, AnyBox>,
pub(crate) entities: EntityMap,
+ pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners:
@@ -252,6 +254,7 @@ impl AppContext {
text_style_stack: Vec::new(),
globals_by_type: HashMap::default(),
entities,
+ new_view_observers: SubscriberSet::new(),
windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())),
global_action_listeners: HashMap::default(),
@@ -610,6 +613,7 @@ impl AppContext {
fn apply_notify_effect(&mut self, emitter: EntityId) {
self.pending_notifications.remove(&emitter);
+
self.observers
.clone()
.retain(&emitter, |handler| handler(self));
@@ -855,6 +859,23 @@ impl AppContext {
self.globals_by_type.insert(global_type, lease.global);
}
+ pub fn observe_new_views<V: 'static>(
+ &mut self,
+ on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
+ ) -> Subscription {
+ self.new_view_observers.insert(
+ TypeId::of::<V>(),
+ Box::new(move |any_view: AnyView, cx: &mut WindowContext| {
+ any_view
+ .downcast::<V>()
+ .unwrap()
+ .update(cx, |view_state, cx| {
+ on_new(view_state, cx);
+ })
+ }),
+ )
+ }
+
pub fn observe_release<E, T>(
&mut self,
handle: &E,
@@ -258,7 +258,7 @@ impl VisualContext for AsyncWindowContext {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static,
+ V: 'static + Render,
{
self.window
.update(self, |_, cx| cx.build_view(build_view_state))
@@ -1,4 +1,7 @@
-use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, ViewContext};
+use crate::{
+ BorrowWindow, Bounds, ElementId, FocusHandle, InputHandlerView, LayoutId, Pixels, ViewContext,
+ WindowInputHandler,
+};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
use std::{any::Any, mem};
@@ -31,6 +34,14 @@ pub trait Element<V: 'static> {
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
);
+
+ fn handle_text_input<'a>(
+ &self,
+ _view_state: &'a mut V,
+ _cx: &mut ViewContext<V>,
+ ) -> Option<(Box<dyn InputHandlerView>, &'a FocusHandle)> {
+ None
+ }
}
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
@@ -154,6 +165,18 @@ where
mut frame_state,
} => {
let bounds = cx.layout_bounds(layout_id);
+ if let Some((input_handler, focus_handle)) =
+ self.element.handle_text_input(view_state, cx)
+ {
+ if focus_handle.is_focused(cx) {
+ cx.window.requested_input_handler = Some(Box::new(WindowInputHandler {
+ cx: cx.app.this.clone(),
+ window: cx.window_handle(),
+ input_handler,
+ element_bounds: bounds,
+ }));
+ }
+ }
if let Some(id) = self.element.id() {
cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
@@ -112,7 +112,7 @@ pub trait VisualContext: Context {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static;
+ V: 'static + Render;
fn update_view<V: 'static, R>(
&mut self,
@@ -305,7 +305,7 @@ pub trait PlatformInputHandler {
new_selected_range: Option<Range<usize>>,
);
fn unmark_text(&mut self);
- fn bounds_for_range(&self, range_utf16: Range<usize>) -> Option<Bounds<f32>>;
+ fn bounds_for_range(&self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
}
#[derive(Debug)]
@@ -1484,10 +1484,12 @@ extern "C" fn first_rect_for_character_range(
|bounds| {
NSRect::new(
NSPoint::new(
- frame.origin.x + bounds.origin.x as f64,
- frame.origin.y + frame.size.height - bounds.origin.y as f64,
+ frame.origin.x + bounds.origin.x.0 as f64,
+ frame.origin.y + frame.size.height
+ - bounds.origin.y.0 as f64
+ - bounds.size.height.0 as f64,
),
- NSSize::new(bounds.size.width as f64, bounds.size.height as f64),
+ NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
)
},
)
@@ -5,7 +5,7 @@ use crate::{
};
use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
-use smallvec::smallvec;
+use smallvec::{smallvec, SmallVec};
pub trait Styled {
fn style(&mut self) -> &mut StyleRefinement;
@@ -295,24 +295,11 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
- fn shadow(mut self) -> Self
+ fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self
where
Self: Sized,
{
- self.style().box_shadow = Some(smallvec![
- BoxShadow {
- color: hsla(0., 0., 0., 0.1),
- offset: point(px(0.), px(1.)),
- blur_radius: px(3.),
- spread_radius: px(0.),
- },
- BoxShadow {
- color: hsla(0., 0., 0., 0.1),
- offset: point(px(0.), px(1.)),
- blur_radius: px(2.),
- spread_radius: px(-1.),
- }
- ]);
+ self.style().box_shadow = Some(shadows);
self
}
@@ -2,14 +2,13 @@ use crate::{
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId,
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId,
- GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, InputHandler, IsZero, KeyListener,
- KeyMatch, KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite,
- MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
- PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel,
- Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
- SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription,
- TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
- WindowBounds, WindowInputHandler, WindowOptions, SUBPIXEL_VARIANTS,
+ GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
+ KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
+ PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
+ RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow,
+ SharedString, Size, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline,
+ UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Result};
use collections::HashMap;
@@ -212,7 +211,7 @@ pub struct Window {
default_prevented: bool,
mouse_position: Point<Pixels>,
requested_cursor_style: Option<CursorStyle>,
- requested_input_handler: Option<Box<dyn PlatformInputHandler>>,
+ pub(crate) requested_input_handler: Option<Box<dyn PlatformInputHandler>>,
scale_factor: f32,
bounds: WindowBounds,
bounds_observers: SubscriberSet<(), AnyObserver>,
@@ -1442,7 +1441,7 @@ impl VisualContext for WindowContext<'_> {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static,
+ V: 'static + Render,
{
let slot = self.app.entities.reserve();
let view = View {
@@ -1450,7 +1449,16 @@ impl VisualContext for WindowContext<'_> {
};
let mut cx = ViewContext::new(&mut *self.app, &mut *self.window, &view);
let entity = build_view_state(&mut cx);
- self.entities.insert(slot, entity);
+ cx.entities.insert(slot, entity);
+
+ cx.new_view_observers
+ .clone()
+ .retain(&TypeId::of::<V>(), |observer| {
+ let any_view = AnyView::from(view.clone());
+ (observer)(any_view, self);
+ true
+ });
+
view
}
@@ -2176,19 +2184,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
}
}
-impl<V> ViewContext<'_, V>
-where
- V: InputHandler + 'static,
-{
- pub fn handle_text_input(&mut self) {
- self.window.requested_input_handler = Some(Box::new(WindowInputHandler {
- cx: self.app.this.clone(),
- window: self.window_handle(),
- handler: self.view().downgrade(),
- }));
- }
-}
-
impl<V> ViewContext<'_, V> {
pub fn emit<Evt>(&mut self, event: Evt)
where
@@ -2242,7 +2237,7 @@ impl<V> Context for ViewContext<'_, V> {
}
impl<V: 'static> VisualContext for ViewContext<'_, V> {
- fn build_view<W: 'static>(
+ fn build_view<W: Render + 'static>(
&mut self,
build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>> {
@@ -1,89 +1,167 @@
-use crate::{AnyWindowHandle, AppCell, Context, PlatformInputHandler, ViewContext, WeakView};
+use crate::{
+ AnyWindowHandle, AppCell, Bounds, Context, Pixels, PlatformInputHandler, View, ViewContext,
+ WindowContext,
+};
use std::{ops::Range, rc::Weak};
-pub struct WindowInputHandler<V>
-where
- V: InputHandler,
-{
+pub struct WindowInputHandler {
pub cx: Weak<AppCell>,
+ pub input_handler: Box<dyn InputHandlerView>,
pub window: AnyWindowHandle,
- pub handler: WeakView<V>,
+ pub element_bounds: Bounds<Pixels>,
}
-impl<V: InputHandler + 'static> PlatformInputHandler for WindowInputHandler<V> {
- fn selected_text_range(&self) -> Option<std::ops::Range<usize>> {
- self.update(|view, cx| view.selected_text_range(cx))
- .flatten()
+pub trait InputHandlerView {
+ fn text_for_range(&self, range: Range<usize>, cx: &mut WindowContext) -> Option<String>;
+ fn selected_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>>;
+ fn marked_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>>;
+ fn unmark_text(&self, cx: &mut WindowContext);
+ fn replace_text_in_range(
+ &self,
+ range: Option<Range<usize>>,
+ text: &str,
+ cx: &mut WindowContext,
+ );
+ fn replace_and_mark_text_in_range(
+ &self,
+ range: Option<Range<usize>>,
+ new_text: &str,
+ new_selected_range: Option<Range<usize>>,
+ cx: &mut WindowContext,
+ );
+ fn bounds_for_range(
+ &self,
+ range_utf16: std::ops::Range<usize>,
+ element_bounds: crate::Bounds<Pixels>,
+ cx: &mut WindowContext,
+ ) -> Option<crate::Bounds<Pixels>>;
+}
+
+pub trait InputHandler: Sized {
+ fn text_for_range(&self, range: Range<usize>, cx: &mut ViewContext<Self>) -> Option<String>;
+ fn selected_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+ fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+ fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
+ fn replace_text_in_range(
+ &mut self,
+ range: Option<Range<usize>>,
+ text: &str,
+ cx: &mut ViewContext<Self>,
+ );
+ fn replace_and_mark_text_in_range(
+ &mut self,
+ range: Option<Range<usize>>,
+ new_text: &str,
+ new_selected_range: Option<Range<usize>>,
+ cx: &mut ViewContext<Self>,
+ );
+ fn bounds_for_range(
+ &mut self,
+ range_utf16: std::ops::Range<usize>,
+ element_bounds: crate::Bounds<Pixels>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<crate::Bounds<Pixels>>;
+}
+
+impl<V: InputHandler + 'static> InputHandlerView for View<V> {
+ fn text_for_range(&self, range: Range<usize>, cx: &mut WindowContext) -> Option<String> {
+ self.update(cx, |this, cx| this.text_for_range(range, cx))
}
- fn marked_text_range(&self) -> Option<std::ops::Range<usize>> {
- self.update(|view, cx| view.marked_text_range(cx)).flatten()
+ fn selected_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>> {
+ self.update(cx, |this, cx| this.selected_text_range(cx))
}
- fn text_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<String> {
- self.update(|view, cx| view.text_for_range(range_utf16, cx))
- .flatten()
+ fn marked_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>> {
+ self.update(cx, |this, cx| this.marked_text_range(cx))
+ }
+
+ fn unmark_text(&self, cx: &mut WindowContext) {
+ self.update(cx, |this, cx| this.unmark_text(cx))
}
fn replace_text_in_range(
- &mut self,
- replacement_range: Option<std::ops::Range<usize>>,
+ &self,
+ range: Option<Range<usize>>,
text: &str,
+ cx: &mut WindowContext,
) {
- self.update(|view, cx| view.replace_text_in_range(replacement_range, text, cx));
+ self.update(cx, |this, cx| this.replace_text_in_range(range, text, cx))
}
fn replace_and_mark_text_in_range(
- &mut self,
- range_utf16: Option<std::ops::Range<usize>>,
+ &self,
+ range: Option<Range<usize>>,
new_text: &str,
- new_selected_range: Option<std::ops::Range<usize>>,
+ new_selected_range: Option<Range<usize>>,
+ cx: &mut WindowContext,
) {
- self.update(|view, cx| {
- view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
- });
+ self.update(cx, |this, cx| {
+ this.replace_and_mark_text_in_range(range, new_text, new_selected_range, cx)
+ })
}
- fn unmark_text(&mut self) {
- self.update(|view, cx| view.unmark_text(cx));
+ fn bounds_for_range(
+ &self,
+ range_utf16: std::ops::Range<usize>,
+ element_bounds: crate::Bounds<Pixels>,
+ cx: &mut WindowContext,
+ ) -> Option<crate::Bounds<Pixels>> {
+ self.update(cx, |this, cx| {
+ this.bounds_for_range(range_utf16, element_bounds, cx)
+ })
}
+}
- fn bounds_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<crate::Bounds<f32>> {
- self.update(|view, cx| view.bounds_for_range(range_utf16, cx))
+impl PlatformInputHandler for WindowInputHandler {
+ fn selected_text_range(&self) -> Option<Range<usize>> {
+ self.update(|handler, cx| handler.selected_text_range(cx))
.flatten()
}
-}
-impl<V: InputHandler + 'static> WindowInputHandler<V> {
- fn update<T>(&self, f: impl FnOnce(&mut V, &mut ViewContext<V>) -> T) -> Option<T> {
- let cx = self.cx.upgrade()?;
- let mut cx = cx.borrow_mut();
- cx.update_window(self.window, |_, cx| self.handler.update(cx, f).ok())
- .ok()?
+ fn marked_text_range(&self) -> Option<Range<usize>> {
+ self.update(|handler, cx| handler.marked_text_range(cx))
+ .flatten()
+ }
+
+ fn text_for_range(&self, range_utf16: Range<usize>) -> Option<String> {
+ self.update(|handler, cx| handler.text_for_range(range_utf16, cx))
+ .flatten()
+ }
+
+ fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
+ self.update(|handler, cx| handler.replace_text_in_range(replacement_range, text, cx));
}
-}
-pub trait InputHandler: Sized {
- fn text_for_range(&self, range: Range<usize>, cx: &mut ViewContext<Self>) -> Option<String>;
- fn selected_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
- fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
- fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
- fn replace_text_in_range(
- &mut self,
- range: Option<Range<usize>>,
- text: &str,
- cx: &mut ViewContext<Self>,
- );
fn replace_and_mark_text_in_range(
&mut self,
- range: Option<Range<usize>>,
+ range_utf16: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
- cx: &mut ViewContext<Self>,
- );
- fn bounds_for_range(
+ ) {
+ self.update(|handler, cx| {
+ handler.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
+ });
+ }
+
+ fn unmark_text(&mut self) {
+ self.update(|handler, cx| handler.unmark_text(cx));
+ }
+
+ fn bounds_for_range(&self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
+ self.update(|handler, cx| handler.bounds_for_range(range_utf16, self.element_bounds, cx))
+ .flatten()
+ }
+}
+
+impl WindowInputHandler {
+ fn update<R>(
&self,
- range_utf16: std::ops::Range<usize>,
- cx: &mut ViewContext<Self>,
- ) -> Option<crate::Bounds<f32>>;
+ f: impl FnOnce(&dyn InputHandlerView, &mut WindowContext) -> R,
+ ) -> Option<R> {
+ let cx = self.cx.upgrade()?;
+ let mut cx = cx.borrow_mut();
+ cx.update_window(self.window, |_, cx| f(&*self.input_handler, cx))
+ .ok()
+ }
}
@@ -1,5 +1,10 @@
use gpui::actions;
+// todo!(remove this)
+// https://github.com/rust-lang/rust/issues/47384
+// https://github.com/mmastrac/rust-ctor/issues/280
+pub fn unused() {}
+
actions!(
Cancel,
Confirm,
@@ -71,7 +71,7 @@ impl Theme {
&self.styles.system
}
- /// Returns the [`ThemeColors`] for the theme.
+ /// Returns the [`PlayerColors`] for the theme.
#[inline(always)]
pub fn players(&self) -> &PlayerColors {
&self.styles.player
@@ -3,6 +3,7 @@ mod button;
mod checkbox;
mod context_menu;
mod details;
+mod elevated_surface;
mod facepile;
mod icon;
mod icon_button;
@@ -30,6 +31,7 @@ pub use button::*;
pub use checkbox::*;
pub use context_menu::*;
pub use details::*;
+pub use elevated_surface::*;
pub use facepile::*;
pub use icon::*;
pub use icon_button::*;
@@ -0,0 +1,28 @@
+use gpui::Div;
+
+use crate::{prelude::*, v_stack};
+
+/// Create an elevated surface.
+///
+/// Must be used inside of a relative parent element
+pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Div<V> {
+ let colors = cx.theme().colors();
+
+ // let shadow = BoxShadow {
+ // color: hsla(0., 0., 0., 0.1),
+ // offset: point(px(0.), px(1.)),
+ // blur_radius: px(3.),
+ // spread_radius: px(0.),
+ // };
+
+ v_stack()
+ .rounded_lg()
+ .bg(colors.elevated_surface_background)
+ .border()
+ .border_color(colors.border)
+ .shadow(level.shadow())
+}
+
+pub fn modal<V>(cx: &mut ViewContext<V>) -> Div<V> {
+ elevated_surface(ElevationIndex::ModalSurfaces, cx)
+}
@@ -1,3 +1,6 @@
+use gpui::{hsla, point, px, BoxShadow};
+use smallvec::{smallvec, SmallVec};
+
#[doc = include_str!("elevation.md")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Elevation {
@@ -17,8 +20,8 @@ pub enum ElevationIndex {
}
impl ElevationIndex {
- pub fn usize(&self) -> usize {
- match *self {
+ pub fn z_index(self) -> u32 {
+ match self {
ElevationIndex::AppBackground => 0,
ElevationIndex::UISurface => 100,
ElevationIndex::ElevatedSurface => 200,
@@ -27,6 +30,26 @@ impl ElevationIndex {
ElevationIndex::DraggedElement => 900,
}
}
+
+ pub fn shadow(self) -> SmallVec<[BoxShadow; 2]> {
+ match self {
+ ElevationIndex::AppBackground => smallvec![],
+
+ ElevationIndex::UISurface => smallvec![BoxShadow {
+ color: hsla(0., 0., 0., 0.12),
+ offset: point(px(0.), px(1.)),
+ blur_radius: px(3.),
+ spread_radius: px(0.),
+ }],
+
+ _ => smallvec![BoxShadow {
+ color: hsla(0., 0., 0., 0.32),
+ offset: point(px(1.), px(3.)),
+ blur_radius: px(12.),
+ spread_radius: px(0.),
+ }],
+ }
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -418,6 +418,14 @@ impl Dock {
// }
}
+impl Render for Dock {
+ type Element = Div<Self>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ todo!()
+ }
+}
+
// todo!()
// impl View for Dock {
// fn ui_name() -> &'static str {
@@ -1,35 +1,34 @@
use crate::Workspace;
use gpui::{
- div, AnyView, AppContext, Div, ParentElement, Render, StatelessInteractive, View, ViewContext,
+ div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatelessInteractive,
+ Styled, Subscription, View, ViewContext,
};
use std::{any::TypeId, sync::Arc};
+use ui::v_stack;
-pub struct ModalRegistry {
- registered_modals: Vec<(TypeId, Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>)>,
-}
-
-pub trait Modal {}
-
-#[derive(Clone)]
pub struct ModalLayer {
open_modal: Option<AnyView>,
+ subscription: Option<Subscription>,
+ registered_modals: Vec<(TypeId, Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>)>,
}
-pub fn init_modal_registry(cx: &mut AppContext) {
- cx.set_global(ModalRegistry {
- registered_modals: Vec::new(),
- });
+pub enum ModalEvent {
+ Dismissed,
}
-struct ToggleModal {
- name: String,
-}
+impl ModalLayer {
+ pub fn new() -> Self {
+ Self {
+ open_modal: None,
+ subscription: None,
+ registered_modals: Vec::new(),
+ }
+ }
-impl ModalRegistry {
pub fn register_modal<A: 'static, V, B>(&mut self, action: A, build_view: B)
where
- V: Render,
- B: Fn(&Workspace, &mut ViewContext<Workspace>) -> View<V> + 'static,
+ V: EventEmitter<ModalEvent> + Render,
+ B: Fn(&mut Workspace, &mut ViewContext<Workspace>) -> Option<View<V>> + 'static,
{
let build_view = Arc::new(build_view);
@@ -38,42 +37,56 @@ impl ModalRegistry {
Box::new(move |mut div| {
let build_view = build_view.clone();
- div.on_action(
- move |workspace: &mut Workspace, event: &A, cx: &mut ViewContext<Workspace>| {
- let new_modal = (build_view)(workspace, cx);
- workspace.modal_layer.update(cx, |modal_layer, _| {
- modal_layer.open_modal = Some(new_modal.into());
- });
-
- cx.notify();
- },
- )
+ div.on_action(move |workspace, event: &A, cx| {
+ let Some(new_modal) = (build_view)(workspace, cx) else {
+ return;
+ };
+ workspace.modal_layer().show_modal(new_modal, cx);
+ })
}),
));
}
-}
-impl ModalLayer {
- pub fn new() -> Self {
- Self { open_modal: None }
+ pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Workspace>)
+ where
+ V: EventEmitter<ModalEvent> + Render,
+ {
+ self.subscription = Some(cx.subscribe(&new_modal, |this, modal, e, cx| match e {
+ ModalEvent::Dismissed => this.modal_layer().hide_modal(cx),
+ }));
+ self.open_modal = Some(new_modal.into());
+ cx.notify();
}
- pub fn render(&self, workspace: &Workspace, cx: &ViewContext<Workspace>) -> Div<Workspace> {
- let mut div = div();
-
- // div, c workspace.toggle_modal()div.on_action()) {
- //
- // }
+ pub fn hide_modal(&mut self, cx: &mut ViewContext<Workspace>) {
+ self.open_modal.take();
+ self.subscription.take();
+ cx.notify();
+ }
- // for (type_id, action) in cx.global::<ModalRegistry>().registered_modals.iter() {
- // div = div.useful_on_action(*type_id, action)
- // }
+ pub fn wrapper_element(&self, cx: &ViewContext<Workspace>) -> Div<Workspace> {
+ let mut parent = div().relative().size_full();
- for (_, action) in cx.global::<ModalRegistry>().registered_modals.iter() {
- div = (action)(div);
+ for (_, action) in self.registered_modals.iter() {
+ parent = (action)(parent);
}
- div.children(self.open_modal.clone())
+ parent.when_some(self.open_modal.as_ref(), |parent, open_modal| {
+ let container1 = div()
+ .absolute()
+ .flex()
+ .flex_col()
+ .items_center()
+ .size_full()
+ .top_0()
+ .left_0()
+ .z_index(400);
+
+ // transparent layer
+ let container2 = v_stack().h(px(0.0)).relative().top_20();
+
+ parent.child(container1.child(container2.child(open_modal.clone())))
+ })
}
}
@@ -1,5 +1,7 @@
use crate::ItemHandle;
-use gpui::{AnyView, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext};
+use gpui::{
+ AnyView, Div, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
+};
pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
@@ -49,6 +51,14 @@ pub struct Toolbar {
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
}
+impl Render for Toolbar {
+ type Element = Div<Self>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ todo!()
+ }
+}
+
// todo!()
// impl View for Toolbar {
// fn ui_name() -> &'static str {
@@ -125,61 +135,6 @@ pub struct Toolbar {
// }
// }
-// <<<<<<< HEAD
-// =======
-// #[allow(clippy::too_many_arguments)]
-// fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>)>(
-// svg_path: &'static str,
-// style: theme::Interactive<theme::IconButton>,
-// nav_button_height: f32,
-// tooltip_style: TooltipStyle,
-// enabled: bool,
-// spacing: f32,
-// on_click: F,
-// tooltip_action: A,
-// action_name: &'static str,
-// cx: &mut ViewContext<Toolbar>,
-// ) -> AnyElement<Toolbar> {
-// MouseEventHandler::new::<A, _>(0, cx, |state, _| {
-// let style = if enabled {
-// style.style_for(state)
-// } else {
-// style.disabled_style()
-// };
-// Svg::new(svg_path)
-// .with_color(style.color)
-// .constrained()
-// .with_width(style.icon_width)
-// .aligned()
-// .contained()
-// .with_style(style.container)
-// .constrained()
-// .with_width(style.button_width)
-// .with_height(nav_button_height)
-// .aligned()
-// .top()
-// })
-// .with_cursor_style(if enabled {
-// CursorStyle::PointingHand
-// } else {
-// CursorStyle::default()
-// })
-// .on_click(MouseButton::Left, move |_, toolbar, cx| {
-// on_click(toolbar, cx)
-// })
-// .with_tooltip::<A>(
-// 0,
-// action_name,
-// Some(Box::new(tooltip_action)),
-// tooltip_style,
-// cx,
-// )
-// .contained()
-// .with_margin_right(spacing)
-// .into_any_named("nav button")
-// }
-
-// >>>>>>> 139cbbfd3aebd0863a7d51b0c12d748764cf0b2e
impl Toolbar {
pub fn new() -> Self {
Self {
@@ -46,8 +46,7 @@ use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings,
use itertools::Itertools;
use language2::LanguageRegistry;
use lazy_static::lazy_static;
-pub use modal_layer::ModalRegistry;
-use modal_layer::{init_modal_registry, ModalLayer};
+pub use modal_layer::*;
use node_runtime::NodeRuntime;
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
pub use pane::*;
@@ -224,7 +223,6 @@ pub fn init_settings(cx: &mut AppContext) {
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
init_settings(cx);
- init_modal_registry(cx);
pane::init(cx);
notifications::init(cx);
@@ -544,7 +542,7 @@ pub struct Workspace {
last_active_center_pane: Option<WeakView<Pane>>,
last_active_view_id: Option<proto::ViewId>,
status_bar: View<StatusBar>,
- modal_layer: View<ModalLayer>,
+ modal_layer: ModalLayer,
// titlebar_item: Option<AnyViewHandle>,
notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
project: Model<Project>,
@@ -695,7 +693,8 @@ impl Workspace {
status_bar
});
- let modal_layer = cx.build_view(|cx| ModalLayer::new());
+ let workspace_handle = cx.view().downgrade();
+ let modal_layer = ModalLayer::new();
// todo!()
// cx.update_default_global::<DragAndDrop<Workspace>, _, _>(|drag_and_drop, _| {
@@ -779,6 +778,10 @@ impl Workspace {
}
}
+ pub fn modal_layer(&mut self) -> &mut ModalLayer {
+ &mut self.modal_layer
+ }
+
fn new_local(
abs_paths: Vec<PathBuf>,
app_state: Arc<AppState>,
@@ -3719,13 +3722,13 @@ impl Render for Workspace {
.bg(cx.theme().colors().background)
.child(self.render_titlebar(cx))
.child(
+ // todo! should this be a component a view?
self.modal_layer
- .read(cx)
- .render(self, cx)
+ .wrapper_element(cx)
+ .relative()
.flex_1()
.w_full()
.flex()
- .flex_row()
.overflow_hidden()
.border_t()
.border_b()
@@ -48,6 +48,7 @@ journal = { package = "journal2", path = "../journal2" }
language = { package = "language2", path = "../language2" }
# language_selector = { path = "../language_selector" }
lsp = { package = "lsp2", path = "../lsp2" }
+menu = { package = "menu2", path = "../menu2" }
language_tools = { path = "../language_tools" }
node_runtime = { path = "../node_runtime" }
# assistant = { path = "../assistant" }
@@ -56,6 +56,10 @@ use zed2::{
mod open_listener;
fn main() {
+ //TODO!(figure out what the linker issues are here)
+ // https://github.com/rust-lang/rust/issues/47384
+ // https://github.com/mmastrac/rust-ctor/issues/280
+ menu::unused();
let http = http::client();
init_paths();
init_logger();
@@ -23,7 +23,7 @@ export default function assistant(): any {
const theme = useTheme()
const interactive_role = (
- color: StyleSets
+ color: StyleSets,
): Interactive<RoleCycleButton> => {
return interactive({
base: {
@@ -94,7 +94,7 @@ export default function assistant(): any {
margin: { left: 8, right: 18 },
color: foreground(theme.highest, "positive"),
width: 12,
- }
+ },
},
retrieve_context: toggleable({
base: interactive({
@@ -106,7 +106,8 @@ export default function assistant(): any {
background: background(theme.highest, "on"),
corner_radius: 2,
border: {
- width: 1., color: background(theme.highest, "on")
+ width: 1,
+ color: background(theme.highest, "on"),
},
margin: { left: 2 },
padding: {
@@ -118,17 +119,45 @@ export default function assistant(): any {
},
state: {
hovered: {
- ...text(theme.highest, "mono", "variant", "hovered"),
- background: background(theme.highest, "on", "hovered"),
+ ...text(
+ theme.highest,
+ "mono",
+ "variant",
+ "hovered",
+ ),
+ background: background(
+ theme.highest,
+ "on",
+ "hovered",
+ ),
border: {
- width: 1., color: background(theme.highest, "on", "hovered")
+ width: 1,
+ color: background(
+ theme.highest,
+ "on",
+ "hovered",
+ ),
},
},
clicked: {
- ...text(theme.highest, "mono", "variant", "pressed"),
- background: background(theme.highest, "on", "pressed"),
+ ...text(
+ theme.highest,
+ "mono",
+ "variant",
+ "pressed",
+ ),
+ background: background(
+ theme.highest,
+ "on",
+ "pressed",
+ ),
border: {
- width: 1., color: background(theme.highest, "on", "pressed")
+ width: 1,
+ color: background(
+ theme.highest,
+ "on",
+ "pressed",
+ ),
},
},
},
@@ -143,11 +172,19 @@ export default function assistant(): any {
border: border(theme.highest, "accent"),
},
hovered: {
- background: background(theme.highest, "accent", "hovered"),
+ background: background(
+ theme.highest,
+ "accent",
+ "hovered",
+ ),
border: border(theme.highest, "accent", "hovered"),
},
clicked: {
- background: background(theme.highest, "accent", "pressed"),
+ background: background(
+ theme.highest,
+ "accent",
+ "pressed",
+ ),
border: border(theme.highest, "accent", "pressed"),
},
},
@@ -163,7 +200,8 @@ export default function assistant(): any {
background: background(theme.highest, "on"),
corner_radius: 2,
border: {
- width: 1., color: background(theme.highest, "on")
+ width: 1,
+ color: background(theme.highest, "on"),
},
padding: {
left: 4,
@@ -174,17 +212,45 @@ export default function assistant(): any {
},
state: {
hovered: {
- ...text(theme.highest, "mono", "variant", "hovered"),
- background: background(theme.highest, "on", "hovered"),
+ ...text(
+ theme.highest,
+ "mono",
+ "variant",
+ "hovered",
+ ),
+ background: background(
+ theme.highest,
+ "on",
+ "hovered",
+ ),
border: {
- width: 1., color: background(theme.highest, "on", "hovered")
+ width: 1,
+ color: background(
+ theme.highest,
+ "on",
+ "hovered",
+ ),
},
},
clicked: {
- ...text(theme.highest, "mono", "variant", "pressed"),
- background: background(theme.highest, "on", "pressed"),
+ ...text(
+ theme.highest,
+ "mono",
+ "variant",
+ "pressed",
+ ),
+ background: background(
+ theme.highest,
+ "on",
+ "pressed",
+ ),
border: {
- width: 1., color: background(theme.highest, "on", "pressed")
+ width: 1,
+ color: background(
+ theme.highest,
+ "on",
+ "pressed",
+ ),
},
},
},
@@ -199,11 +265,19 @@ export default function assistant(): any {
border: border(theme.highest, "accent"),
},
hovered: {
- background: background(theme.highest, "accent", "hovered"),
+ background: background(
+ theme.highest,
+ "accent",
+ "hovered",
+ ),
border: border(theme.highest, "accent", "hovered"),
},
clicked: {
- background: background(theme.highest, "accent", "pressed"),
+ background: background(
+ theme.highest,
+ "accent",
+ "pressed",
+ ),
border: border(theme.highest, "accent", "pressed"),
},
},
@@ -78,33 +78,33 @@ export default function status_bar(): any {
padding: { top: 2, bottom: 2, left: 6, right: 6 },
},
container_warning: diagnostic_status_container,
- container_error: diagnostic_status_container
+ container_error: diagnostic_status_container,
},
state: {
hovered: {
icon_color_ok: foreground(layer, "on"),
container_ok: {
- background: background(layer, "hovered")
+ background: background(layer, "hovered"),
},
container_warning: {
- background: background(layer, "hovered")
+ background: background(layer, "hovered"),
},
container_error: {
- background: background(layer, "hovered")
+ background: background(layer, "hovered"),
},
},
clicked: {
icon_color_ok: foreground(layer, "on"),
container_ok: {
- background: background(layer, "pressed")
+ background: background(layer, "pressed"),
},
container_warning: {
- background: background(layer, "pressed")
+ background: background(layer, "pressed"),
},
container_error: {
- background: background(layer, "pressed")
- }
- }
+ background: background(layer, "pressed"),
+ },
+ },
},
}),
panel_buttons: {
@@ -31,7 +31,7 @@ export const theme: ThemeConfig = {
color.muted,
color.subtle,
color.text,
- ].reverse()
+ ].reverse(),
)
.domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]),
red: color_ramp(chroma(color.love)),
@@ -10,49 +10,49 @@ use element::Element;
use frame::frame;
use gpui::{
geometry::{rect::RectF, vector::vec2f},
- platform::WindowOptions,
+ platform::WindowOptions,aa
};
-use log::LevelFilter;
+use log::LevelFilter;a
use simplelog::SimpleLogger;
use themes::{rose_pine, ThemeColors};
-use view::view;
+use view::view;a
mod adapter {
use crate::element::AnyElement;
use crate::element::{LayoutContext, PaintContext};
- use gpui::{geometry::rect::RectF, LayoutEngine};
+ use gpui::{geometry::rect::RectF, LayoutEngine};aaaa
use util::ResultExt;
pub struct Adapter<V>(pub(crate) AnyElement<V>);
- impl<V: 'static> gpui::Element<V> for Adapter<V> {
- type LayoutState = Option<LayoutEngine>;
+ impl<V: 'static> gpui::Element<V> for Adapter<V> {aa
+ type LayoutState = Option<LayaoutEngine>;
type PaintState = ();
fn layout(
&mut self,
constraint: gpui::SizeConstraint,
view: &mut V,
- cx: &mut LayoutContext<V>,
+ cx: &mut LayoutContext<V>,aa
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
cx.push_layout_engine(LayoutEngine::new());
- let node = self.0.layout(view, cx).log_err();
+ let node = self.0.layout(view, cx).log_err();a
if let Some(node) = node {
let layout_engine = cx.layout_engine().unwrap();
layout_engine.compute_layout(node, constraint.max).log_err();
}
let layout_engine = cx.pop_layout_engine();
- if true {
+ if true {a
if !layout_engine.is_some() {
::core::panicking::panic("assertion failed: layout_engine.is_some()")
}
}
- (constraint.max, layout_engine)
+ (constraint.max, layout_engine)a
}
- fn paint(
+ fn paint(a
&mut self,
scene: &mut gpui::SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
layout_engine: &mut Option<LayoutEngine>,
view: &mut V,
- legacy_cx: &mut gpui::PaintContext<V>,
+ legacy_cx: &mut gpui::PaintContext<V>,aaa
) -> Self::PaintState {
legacy_cx.push_layout_engine(layout_engine.take().unwrap());
let mut cx = PaintContext::new(legacy_cx, scene);