2 fix block rendering: computed heights, scrollable column, set_text guard
This commit is contained in:
parent
80fd148280
commit
a390a2cc4a
|
|
@ -334,6 +334,12 @@ impl EditorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text(&mut self, text: &str) {
|
pub fn set_text(&mut self, text: &str) {
|
||||||
|
if text.is_empty() && !self.blocks.is_empty() {
|
||||||
|
// Swift sends empty text for untitled docs — keep initial content if present
|
||||||
|
if self.blocks.len() > 1 || !self.blocks[0].content.text().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if self.blocks.is_empty() {
|
if self.blocks.is_empty() {
|
||||||
self.blocks = crate::blocks::parse_blocks(text);
|
self.blocks = crate::blocks::parse_blocks(text);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1001,20 +1007,102 @@ impl EditorState {
|
||||||
fn view_blocks(&self) -> Element<'_, Message, Theme, iced_wgpu::Renderer> {
|
fn view_blocks(&self) -> Element<'_, Message, Theme, iced_wgpu::Renderer> {
|
||||||
use crate::blocks::BlockKind;
|
use crate::blocks::BlockKind;
|
||||||
|
|
||||||
|
let single_text_block = self.blocks.len() == 1
|
||||||
|
&& self.blocks[0].kind == BlockKind::Text;
|
||||||
|
|
||||||
|
let title_bar_h = 38.0_f32;
|
||||||
|
|
||||||
let mut block_elements: Vec<Element<'_, Message, Theme, iced_wgpu::Renderer>> = Vec::new();
|
let mut block_elements: Vec<Element<'_, Message, Theme, iced_wgpu::Renderer>> = Vec::new();
|
||||||
|
|
||||||
|
if !single_text_block && !self.blocks.is_empty() {
|
||||||
|
if self.blocks[0].kind != BlockKind::Text {
|
||||||
|
block_elements.push(
|
||||||
|
iced_widget::container(iced_widget::text(""))
|
||||||
|
.height(Length::Fixed(title_bar_h))
|
||||||
|
.width(Length::Fill)
|
||||||
|
.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (bi, block) in self.blocks.iter().enumerate() {
|
for (bi, block) in self.blocks.iter().enumerate() {
|
||||||
match block.kind {
|
match block.kind {
|
||||||
BlockKind::Text => {
|
BlockKind::Text => {
|
||||||
let top_pad = if bi == 0 { 38.0_f32 } else { 4.0 };
|
|
||||||
let block_idx = bi;
|
let block_idx = bi;
|
||||||
|
let line_h = self.font_size * 1.3;
|
||||||
|
|
||||||
|
if single_text_block {
|
||||||
let editor = iced_widget::text_editor(&block.content)
|
let editor = iced_widget::text_editor(&block.content)
|
||||||
.on_action(move |action| Message::BlockAction(block_idx, action))
|
.on_action(move |action| Message::BlockAction(block_idx, action))
|
||||||
.font(Font::MONOSPACE)
|
.font(Font::MONOSPACE)
|
||||||
.size(self.font_size)
|
.size(self.font_size)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.padding(Padding { top: top_pad, right: 8.0, bottom: 8.0, left: 8.0 })
|
.padding(Padding { top: title_bar_h, right: 8.0, bottom: 8.0, left: 8.0 })
|
||||||
|
.wrapping(Wrapping::Word)
|
||||||
|
.key_binding(macos_key_binding)
|
||||||
|
.style(|_theme, _status| {
|
||||||
|
let p = palette::current();
|
||||||
|
Style {
|
||||||
|
background: Background::Color(p.base),
|
||||||
|
border: Border::default(),
|
||||||
|
placeholder: p.overlay0,
|
||||||
|
value: p.text,
|
||||||
|
selection: Color { a: 0.4, ..p.blue },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let settings = SyntaxSettings {
|
||||||
|
lang: self.lang.clone().unwrap_or_default(),
|
||||||
|
source: block.content.text(),
|
||||||
|
};
|
||||||
|
let editor_el: Element<'_, Message, Theme, iced_wgpu::Renderer> = editor
|
||||||
|
.highlight_with::<SyntaxHighlighter>(
|
||||||
|
settings,
|
||||||
|
|highlight, _theme| Format {
|
||||||
|
color: Some(syntax::highlight_color(highlight.kind)),
|
||||||
|
font: syntax::highlight_font(highlight.kind),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let text = block.content.text();
|
||||||
|
let result_mask: Vec<bool> = text.lines().map(|l| is_result_line(l)).collect();
|
||||||
|
let source_line_count = result_mask.iter().filter(|r| !**r).count();
|
||||||
|
let decors = compute_line_decors(&text);
|
||||||
|
let gutter = Gutter {
|
||||||
|
line_count: block.content.line_count(),
|
||||||
|
source_line_count,
|
||||||
|
font_size: self.font_size,
|
||||||
|
scroll_offset: self.scroll_offset,
|
||||||
|
cursor_line: block.content.cursor().position.line,
|
||||||
|
top_pad: title_bar_h,
|
||||||
|
result_mask,
|
||||||
|
line_decors: decors,
|
||||||
|
};
|
||||||
|
let gw = gutter.gutter_width();
|
||||||
|
|
||||||
|
let gutter_canvas: Element<'_, Message, Theme, iced_wgpu::Renderer> =
|
||||||
|
canvas::Canvas::new(gutter)
|
||||||
|
.width(Length::Fixed(gw))
|
||||||
|
.height(Length::Fill)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
block_elements.push(
|
||||||
|
iced_widget::row![gutter_canvas, editor_el]
|
||||||
|
.height(Length::Fill)
|
||||||
|
.into()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let top_pad = if bi == 0 { title_bar_h } else { 0.0 };
|
||||||
|
let actual_lines = block.content.line_count().max(1);
|
||||||
|
let editor_h = (actual_lines as f32) * line_h + top_pad + 8.0;
|
||||||
|
|
||||||
|
let editor = iced_widget::text_editor(&block.content)
|
||||||
|
.on_action(move |action| Message::BlockAction(block_idx, action))
|
||||||
|
.font(Font::MONOSPACE)
|
||||||
|
.size(self.font_size)
|
||||||
|
.height(Length::Fixed(editor_h))
|
||||||
|
.padding(Padding { top: top_pad, right: 8.0, bottom: 4.0, left: 8.0 })
|
||||||
.wrapping(Wrapping::Word)
|
.wrapping(Wrapping::Word)
|
||||||
.key_binding(macos_key_binding)
|
.key_binding(macos_key_binding)
|
||||||
.style(|_theme, _status| {
|
.style(|_theme, _status| {
|
||||||
|
|
@ -1061,15 +1149,19 @@ impl EditorState {
|
||||||
let gutter_canvas: Element<'_, Message, Theme, iced_wgpu::Renderer> =
|
let gutter_canvas: Element<'_, Message, Theme, iced_wgpu::Renderer> =
|
||||||
canvas::Canvas::new(gutter)
|
canvas::Canvas::new(gutter)
|
||||||
.width(Length::Fixed(gw))
|
.width(Length::Fixed(gw))
|
||||||
.height(Length::Fill)
|
.height(Length::Fixed(editor_h))
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
block_elements.push(
|
block_elements.push(
|
||||||
|
iced_widget::container(
|
||||||
iced_widget::row![gutter_canvas, editor_el]
|
iced_widget::row![gutter_canvas, editor_el]
|
||||||
.height(Length::Fill)
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fixed(editor_h))
|
||||||
.into()
|
.into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BlockKind::Heading => {
|
BlockKind::Heading => {
|
||||||
let level = match block.heading_level {
|
let level = match block.heading_level {
|
||||||
1 => crate::heading_block::HeadingLevel::H1,
|
1 => crate::heading_block::HeadingLevel::H1,
|
||||||
|
|
@ -1093,7 +1185,18 @@ impl EditorState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
BlockKind::EvalResult => {
|
||||||
|
block_elements.push(
|
||||||
|
crate::eval_block::view::<Message>(&block.eval_text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BlockKind::Tree => {
|
||||||
|
if let Some(ref data) = block.tree_data {
|
||||||
|
block_elements.push(
|
||||||
|
crate::tree_block::view::<Message>(data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1102,10 +1205,11 @@ impl EditorState {
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
} else if block_elements.len() == 1 {
|
} else if single_text_block {
|
||||||
block_elements.remove(0)
|
block_elements.remove(0)
|
||||||
} else {
|
} else {
|
||||||
iced_widget::column(block_elements)
|
iced_widget::column(block_elements)
|
||||||
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue