Allow editor to be created in auto-height mode

This commit is contained in:
Antonio Scandurra 2021-08-24 14:17:15 +02:00
parent 405ff1d9db
commit 54b4a4bf6a
4 changed files with 81 additions and 28 deletions

View file

@ -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 {

View file

@ -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<ChannelList>,
active_channel: Option<(ModelHandle<Channel>, Subscription)>,
messages: ListState,
input_editor: ViewHandle<Editor>,
settings: watch::Receiver<Settings>,
}
@ -20,10 +24,12 @@ impl ChatPanel {
settings: watch::Receiver<Settings>,
cx: &mut ViewContext<Self>,
) -> 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()
}
}

View file

@ -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<Self>,
buffer: ModelHandle<Buffer>,
@ -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<Theme>,
pub font_family: FamilyId,
pub font_size: f32,
@ -313,7 +321,14 @@ impl Editor {
pub fn single_line(settings: watch::Receiver<Settings>, cx: &mut ViewContext<Self>) -> 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<Settings>, cx: &mut ViewContext<Self>) -> 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<Self>,
) -> 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<Self>) {
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<Self>) {
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<Self>) {
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<Self>) {
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));

View file

@ -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());