From 54b4a4bf6a8759d9224b37243d7feae0e6bb0dac Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 24 Aug 2021 14:17:15 +0200 Subject: [PATCH] Allow editor to be created in auto-height mode --- gpui/src/presenter.rs | 7 +++++ zed/src/chat_panel.rs | 16 +++++++--- zed/src/editor.rs | 66 ++++++++++++++++++++++++++++----------- zed/src/editor/element.rs | 20 ++++++++---- 4 files changed, 81 insertions(+), 28 deletions(-) diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index f282e4405e..ac4652482c 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -325,6 +325,13 @@ impl SizeConstraint { Axis::Vertical => self.max.y(), } } + + pub fn min_along(&self, axis: Axis) -> f32 { + match axis { + Axis::Horizontal => self.min.x(), + Axis::Vertical => self.min.y(), + } + } } impl ToJson for SizeConstraint { diff --git a/zed/src/chat_panel.rs b/zed/src/chat_panel.rs index 5e9b950b50..458215e72c 100644 --- a/zed/src/chat_panel.rs +++ b/zed/src/chat_panel.rs @@ -1,14 +1,18 @@ use crate::{ channel::{Channel, ChannelEvent, ChannelList, ChannelMessage}, + editor::Editor, Settings, }; -use gpui::{elements::*, Entity, ModelHandle, RenderContext, Subscription, View, ViewContext}; +use gpui::{ + elements::*, Entity, ModelHandle, RenderContext, Subscription, View, ViewContext, ViewHandle, +}; use postage::watch; pub struct ChatPanel { channel_list: ModelHandle, active_channel: Option<(ModelHandle, Subscription)>, messages: ListState, + input_editor: ViewHandle, settings: watch::Receiver, } @@ -20,10 +24,12 @@ impl ChatPanel { settings: watch::Receiver, cx: &mut ViewContext, ) -> Self { + let input_editor = cx.add_view(|cx| Editor::auto_height(settings.clone(), cx)); let mut this = Self { channel_list, - messages: ListState::new(Vec::new()), active_channel: None, + messages: ListState::new(Vec::new()), + input_editor, settings, }; @@ -81,7 +87,7 @@ impl ChatPanel { } fn render_active_channel_messages(&self) -> ElementBox { - Expanded::new(0.8, List::new(self.messages.clone()).boxed()).boxed() + Expanded::new(1., List::new(self.messages.clone()).boxed()).boxed() } fn render_message(&self, message: &ChannelMessage) -> ElementBox { @@ -99,7 +105,9 @@ impl ChatPanel { } fn render_input_box(&self) -> ElementBox { - Empty::new().boxed() + ConstrainedBox::new(ChildView::new(self.input_editor.id()).boxed()) + .with_max_height(100.) + .boxed() } } diff --git a/zed/src/editor.rs b/zed/src/editor.rs index ba71c0ce60..6ac06cd370 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -43,7 +43,7 @@ const MAX_LINE_LEN: usize = 1024; action!(Cancel); action!(Backspace); action!(Delete); -action!(Newline); +action!(Newline, bool); action!(Insert, String); action!(DeleteLine); action!(DeleteToPreviousWordBoundary); @@ -102,7 +102,8 @@ pub fn init(cx: &mut MutableAppContext) { Binding::new("ctrl-h", Backspace, Some("BufferView")), Binding::new("delete", Delete, Some("BufferView")), Binding::new("ctrl-d", Delete, Some("BufferView")), - Binding::new("enter", Newline, Some("BufferView")), + Binding::new("enter", Newline(false), Some("BufferView")), + Binding::new("alt-enter", Newline(true), Some("BufferView")), Binding::new("tab", Insert("\t".into()), Some("BufferView")), Binding::new("ctrl-shift-K", DeleteLine, Some("BufferView")), Binding::new( @@ -268,6 +269,12 @@ pub enum SelectPhase { End, } +enum EditorMode { + SingleLine, + AutoHeight, + Full, +} + pub struct Editor { handle: WeakViewHandle, buffer: ModelHandle, @@ -285,12 +292,13 @@ pub struct Editor { cursors_visible: bool, blink_epoch: usize, blinking_paused: bool, - single_line: bool, + mode: EditorMode, } pub struct Snapshot { pub display_snapshot: DisplayMapSnapshot, pub gutter_visible: bool, + pub auto_height: bool, pub theme: Arc, pub font_family: FamilyId, pub font_size: f32, @@ -313,7 +321,14 @@ impl Editor { pub fn single_line(settings: watch::Receiver, cx: &mut ViewContext) -> Self { let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx)); let mut view = Self::for_buffer(buffer, settings, cx); - view.single_line = true; + view.mode = EditorMode::SingleLine; + view + } + + pub fn auto_height(settings: watch::Receiver, cx: &mut ViewContext) -> Self { + let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx)); + let mut view = Self::for_buffer(buffer, settings, cx); + view.mode = EditorMode::AutoHeight; view } @@ -359,7 +374,7 @@ impl Editor { cursors_visible: false, blink_epoch: 0, blinking_paused: false, - single_line: false, + mode: EditorMode::Full, } } @@ -376,7 +391,8 @@ impl Editor { Snapshot { display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)), - gutter_visible: !self.single_line, + gutter_visible: matches!(self.mode, EditorMode::Full), + auto_height: matches!(self.mode, EditorMode::AutoHeight), scroll_position: self.scroll_position, scroll_top_anchor: self.scroll_top_anchor.clone(), theme: settings.theme.clone(), @@ -413,10 +429,15 @@ impl Editor { line_height: f32, cx: &mut ViewContext, ) -> bool { + let visible_lines = viewport_height / line_height; let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut scroll_position = compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor); - let max_scroll_top = display_map.max_point().row().saturating_sub(1) as f32; + let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight) { + (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.) + } else { + display_map.max_point().row().saturating_sub(1) as f32 + }; if scroll_position.y() > max_scroll_top { scroll_position.set_y(max_scroll_top); self.set_scroll_position(scroll_position, cx); @@ -428,7 +449,6 @@ impl Editor { return false; } - let visible_lines = viewport_height / line_height; let first_cursor_top = self .selections(cx) .first() @@ -445,9 +465,13 @@ impl Editor { .row() as f32 + 1.0; - let margin = ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0) - .floor() - .min(3.0); + let margin = if matches!(self.mode, EditorMode::AutoHeight) { + 0. + } else { + ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0) + .floor() + .min(3.0) + }; if margin < 0.0 { return false; } @@ -695,11 +719,17 @@ impl Editor { self.end_transaction(cx); } - fn newline(&mut self, _: &Newline, cx: &mut ViewContext) { - if self.single_line { - cx.propagate_action(); - } else { - self.insert(&Insert("\n".into()), cx); + fn newline(&mut self, Newline(insert_newline): &Newline, cx: &mut ViewContext) { + match self.mode { + EditorMode::SingleLine => cx.propagate_action(), + EditorMode::AutoHeight => { + if *insert_newline { + self.insert(&Insert("\n".into()), cx); + } else { + cx.propagate_action(); + } + } + EditorMode::Full => self.insert(&Insert("\n".into()), cx), } } @@ -1276,7 +1306,7 @@ impl Editor { pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - if self.single_line { + if matches!(self.mode, EditorMode::SingleLine) { cx.propagate_action(); } else { let mut selections = self.selections(cx.as_ref()).to_vec(); @@ -1317,7 +1347,7 @@ impl Editor { } pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext) { - if self.single_line { + if matches!(self.mode, EditorMode::SingleLine) { cx.propagate_action(); } else { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index 812bd795a7..85e54c71b9 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -1,4 +1,4 @@ -use super::{DisplayPoint, Editor, Select, SelectPhase, Snapshot, Insert, Scroll}; +use super::{DisplayPoint, Editor, EditorMode, Insert, Scroll, Select, SelectPhase, Snapshot}; use crate::time::ReplicaId; use gpui::{ color::Color, @@ -9,8 +9,8 @@ use gpui::{ }, json::{self, ToJson}, text_layout::{self, TextLayoutCache}, - AppContext, Border, Element, Event, EventContext, FontCache, LayoutContext, MutableAppContext, - PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, + AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext, + MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, }; use json::json; use smallvec::SmallVec; @@ -204,7 +204,7 @@ impl EditorElement { corner_radius: 0., }); - if !editor.single_line { + if let EditorMode::Full = editor.mode { let mut active_rows = layout.active_rows.iter().peekable(); while let Some((start_row, contains_non_empty_selection)) = active_rows.next() { let mut end_row = *start_row; @@ -409,8 +409,16 @@ impl Element for EditorElement { snapshot } }); - if size.y().is_infinite() { - size.set_y((snapshot.max_point().row() + 1) as f32 * line_height); + + let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height; + if snapshot.auto_height { + size.set_y( + scroll_height + .min(constraint.max_along(Axis::Vertical)) + .max(constraint.min_along(Axis::Vertical)), + ) + } else if size.y().is_infinite() { + size.set_y(scroll_height); } let gutter_size = vec2f(gutter_width, size.y()); let text_size = vec2f(text_width, size.y());