first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
@@ -1,22 +1,16 @@
use galaxyui::{
AppContext, Element, SizeConstraint,
elements::{
Align, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Empty, Flex, Icon,
ParentElement, Radius, Shrinkable, Text,
},
geometry::vector::vec2f,
};
use crate::{
editor::EmbeddedItemModel,
extract_block,
render::{
element::paint::{CursorData, CursorDisplayType},
model::{BlockItem, RichTextStyles, viewport::ViewportItem},
},
use galaxyui_core::elements::{
Align, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Empty, Flex, Icon,
ParentElement, Radius, Shrinkable, Text,
};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::{AppContext, Element, SizeConstraint};
use super::RenderableBlock;
use crate::editor::EmbeddedItemModel;
use crate::extract_block;
use crate::render::element::paint::{CursorData, CursorDisplayType};
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RichTextStyles};
pub struct RenderableBrokenEmbedding {
row: Box<dyn Element>,
@@ -79,8 +73,8 @@ impl RenderableBlock for RenderableBrokenEmbedding {
fn layout(
&mut self,
model: &crate::render::model::RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
self.row.layout(
SizeConstraint::strict(vec2f(
@@ -104,7 +98,7 @@ impl RenderableBlock for RenderableBrokenEmbedding {
&mut self,
model: &crate::render::model::RenderState,
ctx: &mut super::RenderContext,
app: &galaxyui::AppContext,
app: &galaxyui_core::AppContext,
) {
let content = model.content();
let broken_link = extract_block!(self.viewport_item, content, (block, BlockItem::Embedded(item)) => block.embedded(item));
@@ -156,21 +150,25 @@ impl RenderableBlock for RenderableBrokenEmbedding {
ctx.paint
.scene
.start_layer(galaxyui::ClipBounds::ActiveLayer);
.start_layer(galaxyui_core::ClipBounds::ActiveLayer);
self.row
.paint(ctx.content_to_screen(content_origin), ctx.paint, app);
ctx.paint.scene.stop_layer();
}
fn after_layout(&mut self, ctx: &mut galaxyui::AfterLayoutContext, app: &galaxyui::AppContext) {
fn after_layout(
&mut self,
ctx: &mut galaxyui_core::AfterLayoutContext,
app: &galaxyui_core::AppContext,
) {
self.row.after_layout(ctx, app);
}
fn dispatch_event(
&mut self,
_model: &crate::render::model::RenderState,
event: &galaxyui::event::DispatchedEvent,
ctx: &mut galaxyui::EventContext,
event: &galaxyui_core::event::DispatchedEvent,
ctx: &mut galaxyui_core::EventContext,
app: &AppContext,
) -> bool {
self.row.dispatch_event(event, ctx, app)
+16 -17
View File
@@ -1,17 +1,11 @@
use crate::{
content::text::BufferBlockStyle,
extract_block,
render::{
element::paint::CursorData,
model::{BlockItem, RenderState, viewport::ViewportItem},
},
};
use super::{
RenderContext, RenderableBlock,
paragraph::paragraph_placeholder_text,
placeholder::{self, BlockPlaceholder},
};
use super::paragraph::paragraph_placeholder_text;
use super::placeholder::{self, BlockPlaceholder};
use super::{RenderContext, RenderableBlock};
use crate::content::text::BufferBlockStyle;
use crate::extract_block;
use crate::render::element::paint::CursorData;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
/// Renderable representation of invisible rich-text items. This is used for the trailing newline
/// marker.
@@ -37,8 +31,8 @@ impl RenderableBlock for Empty {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
self.placeholder
.layout(&self.viewport_item, model, ctx, app, |_| {
@@ -49,7 +43,12 @@ impl RenderableBlock for Empty {
});
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
let content = model.content();
let cursor = extract_block!(self.viewport_item, content, (block, BlockItem::TrailingNewLine(cursor)) => block.trailing_newline(cursor));
if self.placeholder.paint(cursor.content_origin(), model, ctx) {
+14 -13
View File
@@ -1,13 +1,9 @@
use crate::{
content::text::{BlockHeaderSize, BufferBlockStyle},
extract_block,
render::model::{BlockItem, RenderState, viewport::ViewportItem},
};
use super::{
RenderContext, RenderableBlock,
placeholder::{BlockPlaceholder, Options},
};
use super::placeholder::{BlockPlaceholder, Options};
use super::{RenderContext, RenderableBlock};
use crate::content::text::{BlockHeaderSize, BufferBlockStyle};
use crate::extract_block;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
pub struct RenderableHeader {
viewport_item: ViewportItem,
@@ -31,8 +27,8 @@ impl RenderableBlock for RenderableHeader {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
self.placeholder
.layout(&self.viewport_item, model, ctx, app, |block| {
@@ -53,7 +49,12 @@ impl RenderableBlock for RenderableHeader {
});
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
let content = model.content();
let (paragraph, header_size) = extract_block!(
self.viewport_item, content,
@@ -1,16 +1,17 @@
use crate::extract_block;
use crate::render::model::BlockItem;
use super::super::model::{RenderState, viewport::ViewportItem};
use super::{RenderContext, RenderableBlock};
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::color::internal_colors;
use galaxyui::elements::{CrossAxisAlignment, Empty, Flex, ParentElement};
use galaxyui::{
use galaxyui_core::elements::{Container, CrossAxisAlignment, Empty, Flex, ParentElement};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::{
AfterLayoutContext, AppContext, Element, LayoutContext, SingletonEntity, SizeConstraint,
elements::Container, geometry::vector::vec2f,
};
use super::super::model::RenderState;
use super::super::model::viewport::ViewportItem;
use super::{RenderContext, RenderableBlock};
use crate::extract_block;
use crate::render::model::BlockItem;
/// A renderable block for hidden sections that renders a single- or double-line-height rectangle.
/// This is used for BlockItem::Hidden items that need to be visually indicated.
pub struct RenderableHiddenSection {
@@ -71,8 +72,8 @@ impl RenderableBlock for RenderableHiddenSection {
fn dispatch_event(
&mut self,
_model: &RenderState,
event: &galaxyui::event::DispatchedEvent,
ctx: &mut galaxyui::EventContext,
event: &galaxyui_core::event::DispatchedEvent,
ctx: &mut galaxyui_core::EventContext,
app: &AppContext,
) -> bool {
self.element.dispatch_event(event, ctx, app)
@@ -1,20 +1,12 @@
use galaxyui::{
elements::{CornerRadius, Radius},
geometry::{
rect::RectF,
vector::{Vector2F, vec2f},
},
};
use crate::{
extract_block,
render::{
element::paint::{CursorData, CursorDisplayType},
model::{BlockItem, RenderState, RichTextStyles, viewport::ViewportItem},
},
};
use galaxyui_core::elements::{CornerRadius, Radius};
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use super::{RenderContext, RenderableBlock};
use crate::extract_block;
use crate::render::element::paint::{CursorData, CursorDisplayType};
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState, RichTextStyles};
/// Renderable representation of a single horizontal rule separator.
pub struct HorizontalRule {
@@ -77,12 +69,17 @@ impl RenderableBlock for HorizontalRule {
fn layout(
&mut self,
_model: &RenderState,
_ctx: &mut galaxyui::LayoutContext,
_app: &galaxyui::AppContext,
_ctx: &mut galaxyui_core::LayoutContext,
_app: &galaxyui_core::AppContext,
) {
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
let content = model.content();
let horizontal_rule = extract_block!(self.viewport_item, content, (block, BlockItem::HorizontalRule(rule)) => block.horizontal_rule(rule));
+16 -17
View File
@@ -1,18 +1,12 @@
use galaxyui::{
Element, SizeConstraint,
elements::{CacheOption, Image},
geometry::vector::vec2f,
};
use crate::{
extract_block,
render::{
element::paint::{CursorData, CursorDisplayType},
model::{BlockItem, RenderState, viewport::ViewportItem},
},
};
use galaxyui_core::elements::{CacheOption, Image};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::{Element, SizeConstraint};
use super::{RenderContext, RenderableBlock};
use crate::extract_block;
use crate::render::element::paint::{CursorData, CursorDisplayType};
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
pub struct RenderableImage {
viewport_item: ViewportItem,
@@ -39,8 +33,8 @@ impl RenderableBlock for RenderableImage {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
let content = model.content();
let (asset_source, config) = extract_block!(
@@ -60,7 +54,12 @@ impl RenderableBlock for RenderableImage {
self.image_element = Some(Box::new(image));
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
app: &galaxyui_core::AppContext,
) {
let content = model.content();
let positioned_image = extract_block!(
self.viewport_item,
@@ -83,7 +82,7 @@ impl RenderableBlock for RenderableImage {
}
if selected {
let rect_bounds = galaxyui::geometry::rect::RectF::new(screen_position, size);
let rect_bounds = galaxyui_core::geometry::rect::RectF::new(screen_position, size);
ctx.paint
.scene
.draw_rect_with_hit_recording(rect_bounds)
@@ -1,24 +1,20 @@
use std::ops::Range;
use galaxyui::{
use galaxyui_core::elements::Point;
use galaxyui_core::event::DispatchedEvent;
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::Vector2F;
use galaxyui_core::units::IntoPixels;
use galaxyui_core::{
AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, ModelHandle,
PaintContext, SizeConstraint, WeakViewHandle,
elements::Point,
event::DispatchedEvent,
geometry::{rect::RectF, vector::Vector2F},
units::IntoPixels,
};
use crate::{
editor::EditorView,
render::{
element::{
DisplayOptions, RenderContext, RenderableBlock, paragraph::RenderableParagraph,
temporary_block::RenderableTemporaryBlock,
},
model::{BlockItem, RenderLineLocation, RenderState},
},
};
use crate::editor::EditorView;
use crate::render::element::paragraph::RenderableParagraph;
use crate::render::element::temporary_block::RenderableTemporaryBlock;
use crate::render::element::{DisplayOptions, RenderContext, RenderableBlock};
use crate::render::model::{BlockItem, RenderLineLocation, RenderState};
pub struct RichTextElementLens<V: EditorView> {
blocks: Option<Vec<Box<dyn RenderableBlock>>>,
+101 -20
View File
@@ -1,29 +1,39 @@
use galaxyui::{
AppContext, Element, SizeConstraint,
elements::{Align, CacheOption, CornerRadius, Image, Radius, Text},
geometry::vector::vec2f,
};
use std::time::Duration;
use crate::{
extract_block,
render::{
element::paint::CursorData,
model::{BlockItem, RenderState, viewport::ViewportItem},
},
};
use galaxyui_core::elements::{Align, CacheOption, CornerRadius, Empty, Image, Radius, Text};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::{AppContext, Element, SizeConstraint};
use super::{CursorDisplayType, RenderContext, RenderableBlock};
use super::{RenderContext, RenderableBlock};
use crate::editor::RunnableCommandModel;
use crate::extract_block;
use crate::render::BLOCK_FOOTER_HEIGHT;
use crate::render::element::paint::{CursorData, CursorDisplayType};
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState, bounds};
const MERMAID_RENDER_TIMEOUT: Duration = Duration::from_secs(10);
pub struct RenderableMermaidDiagram {
viewport_item: ViewportItem,
image_element: Option<Box<dyn Element>>,
footer: Box<dyn Element>,
}
impl RenderableMermaidDiagram {
pub fn new(viewport_item: ViewportItem) -> Self {
pub fn new(
viewport_item: ViewportItem,
model: Option<&dyn RunnableCommandModel>,
editor_is_focused: bool,
ctx: &AppContext,
) -> Self {
let footer = match model {
Some(model) => model.render_block_footer(editor_is_focused, ctx),
None => Empty::new().finish(),
};
Self {
viewport_item,
image_element: None,
footer,
}
}
}
@@ -33,7 +43,12 @@ impl RenderableBlock for RenderableMermaidDiagram {
&self.viewport_item
}
fn layout(&mut self, model: &RenderState, ctx: &mut galaxyui::LayoutContext, app: &AppContext) {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui_core::LayoutContext,
app: &AppContext,
) {
let content = model.content();
let (asset_source, config) = extract_block!(
self.viewport_item,
@@ -41,14 +56,48 @@ impl RenderableBlock for RenderableMermaidDiagram {
(_block, BlockItem::MermaidDiagram { asset_source, config, .. }) => (asset_source.clone(), *config)
);
self.footer.layout(
SizeConstraint::strict(vec2f(
self.viewport_item.content_size.x(),
BLOCK_FOOTER_HEIGHT,
)),
ctx,
app,
);
let code_text = model.styles().code_text;
let placeholder_color = model.styles().placeholder_color;
let placeholder = Align::new(
Text::new(
"Rendering Mermaid diagram…",
code_text.font_family,
code_text.font_size,
)
.with_color(model.styles().placeholder_color)
.with_color(placeholder_color)
.with_line_height_ratio(code_text.line_height_ratio)
.soft_wrap(false)
.finish(),
)
.finish();
let failure_notice = Align::new(
Text::new(
"Error rendering Mermaid diagram. Please check syntax.",
code_text.font_family,
code_text.font_size,
)
.with_color(placeholder_color)
.with_line_height_ratio(code_text.line_height_ratio)
.soft_wrap(true)
.finish(),
)
.finish();
let timeout_notice = Align::new(
Text::new(
"Failed to render Mermaid diagram",
code_text.font_family,
code_text.font_size,
)
.with_color(placeholder_color)
.with_line_height_ratio(code_text.line_height_ratio)
.soft_wrap(false)
.finish(),
@@ -58,7 +107,9 @@ impl RenderableBlock for RenderableMermaidDiagram {
let size = vec2f(config.width.as_f32(), config.height.as_f32());
let mut image = Image::new(asset_source, CacheOption::BySize)
.contain()
.before_load(placeholder);
.before_load(placeholder)
.on_load_failure(failure_notice)
.on_load_timeout(MERMAID_RENDER_TIMEOUT, timeout_notice);
image.layout(SizeConstraint::strict(size), ctx, app);
self.image_element = Some(Box::new(image));
@@ -96,11 +147,16 @@ impl RenderableBlock for RenderableMermaidDiagram {
.draw_rect_with_hit_recording(content_rect)
.with_background(model.styles().selection_fill);
}
if model.is_selection_head(start_offset) {
let content_position = bounds::content_origin(
self.viewport_item.content_offset,
&self.viewport_item.spacing,
);
let end_of_line_position =
content_position + vec2f(self.viewport_item.content_size.x(), 0.);
ctx.draw_and_save_cursor(
CursorDisplayType::Bar,
content_rect.origin(),
end_of_line_position,
vec2f(
model.styles().cursor_width,
self.viewport_item.content_size.y(),
@@ -109,11 +165,36 @@ impl RenderableBlock for RenderableMermaidDiagram {
model.styles(),
);
}
ctx.paint
.scene
.start_layer(galaxyui_core::ClipBounds::ActiveLayer);
let button_origin = content_rect.lower_right()
- vec2f(
self.footer.size().expect("Footer should be laid out").x(),
0.,
);
self.footer.paint(button_origin, ctx.paint, app);
ctx.paint.scene.stop_layer();
}
fn after_layout(&mut self, ctx: &mut galaxyui::AfterLayoutContext, app: &galaxyui::AppContext) {
fn after_layout(
&mut self,
ctx: &mut galaxyui_core::AfterLayoutContext,
app: &galaxyui_core::AppContext,
) {
if let Some(ref mut image_element) = self.image_element {
image_element.after_layout(ctx, app);
}
self.footer.after_layout(ctx, app);
}
fn dispatch_event(
&mut self,
_model: &RenderState,
event: &galaxyui_core::event::DispatchedEvent,
ctx: &mut galaxyui_core::EventContext,
app: &AppContext,
) -> bool {
self.footer.dispatch_event(event, ctx, app)
}
}
+53 -38
View File
@@ -1,23 +1,29 @@
use std::fmt;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use float_cmp::ApproxEq;
use galaxy_core::ui::theme::Fill as ThemeFill;
use galaxyui::{
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, ModelHandle,
PaintContext, SizeConstraint, WeakViewHandle,
color::ColorU,
elements::{
Axis, Border, Dash, Point, ScrollData, ScrollableElement, Vector2FExt, ZIndex,
new_scrollable::{NewScrollableElement, ScrollableAxis},
},
event::{DispatchedEvent, ModifiersState},
geometry::{
rect::RectF,
vector::{Vector2F, vec2f},
},
platform::Cursor,
units::{IntoPixels, Pixels},
};
use instant::Instant;
use parking_lot::Mutex;
use string_offset::CharOffset;
use temporary_block::RenderableTemporaryBlock;
use vim::vim::VimMode;
use galaxy_core::ui::theme::Fill as ThemeFill;
use galaxyui_core::color::ColorU;
use galaxyui_core::elements::new_scrollable::{NewScrollableElement, ScrollableAxis};
use galaxyui_core::elements::{
Axis, Border, Dash, Point, ScrollData, ScrollableElement, Vector2FExt, ZIndex,
};
use galaxyui_core::event::{DispatchedEvent, ModifiersState};
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use galaxyui_core::platform::Cursor;
use galaxyui_core::units::{IntoPixels, Pixels};
use galaxyui_core::{
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, ModelHandle,
PaintContext, SizeConstraint, WeakViewHandle,
};
use std::{
fmt,
sync::{
@@ -26,26 +32,27 @@ use std::{
},
time::Duration,
};
use temporary_block::RenderableTemporaryBlock;
use vim::vim::VimMode;
use self::empty::Empty;
use self::header::RenderableHeader;
use self::hidden_section::RenderableHiddenSection;
use self::horizontal_rule::HorizontalRule;
use self::image::RenderableImage;
use self::mermaid::RenderableMermaidDiagram;
use self::ordered_list::RenderableOrderedListItem;
pub use self::paint::{CursorData, CursorDisplayType, RenderContext};
use self::paragraph::RenderableParagraph;
use self::runnable_command::RenderableRunnableCommand;
use self::table::RenderableTable;
use self::task_list::RenderableTaskList;
use self::text_block::RenderableTextBlock;
use self::unordered_list::RenderableBulletList;
use super::model::viewport::{SizeInfo, ViewportItem};
use super::model::{
BlockItem, ElementUpdate, HitTestOptions, Location, RenderState, RichTextStyles, UNIT_MARGIN,
viewport::{SizeInfo, ViewportItem},
};
use crate::{content::version::BufferVersion, editor::EditorView};
use string_offset::CharOffset;
use self::{
empty::Empty, header::RenderableHeader, hidden_section::RenderableHiddenSection,
horizontal_rule::HorizontalRule, image::RenderableImage, mermaid::RenderableMermaidDiagram,
ordered_list::RenderableOrderedListItem, paragraph::RenderableParagraph,
runnable_command::RenderableRunnableCommand, table::RenderableTable,
task_list::RenderableTaskList, text_block::RenderableTextBlock,
unordered_list::RenderableBulletList,
};
pub use self::paint::{CursorData, CursorDisplayType, RenderContext};
use crate::content::version::BufferVersion;
use crate::editor::EditorView;
pub mod broken_embedding;
mod empty;
@@ -85,7 +92,7 @@ pub enum VerticalExpansionBehavior {
/// An element that renders rich text, with no additional UI or decorations.
///
/// This element caches the positions listed in [`super::model::saved_positions::SavedPositions`],
/// and the parent view can overlay UI controls on top of them using a [`galaxyui::elements::Stack`].
/// and the parent view can overlay UI controls on top of them using a [`galaxyui_core::elements::Stack`].
///
/// It additionally reserves horizontal gutters, which are considered in-bounds for content hit
/// testing.
@@ -886,7 +893,15 @@ impl<V: EditorView> RichTextElement<V> {
.finish()
}
BlockItem::MermaidDiagram { .. } => {
RenderableMermaidDiagram::new(item).finish()
let start_offset = item.block_offset;
let runnable_command = parent.runnable_command_at(start_offset, ctx);
RenderableMermaidDiagram::new(
item,
runnable_command,
self.display_options.focused,
ctx,
)
.finish()
}
BlockItem::TemporaryBlock {
decoration,
@@ -1077,7 +1092,7 @@ impl<V: EditorView> Element for RichTextElement<V> {
return;
};
ctx.scene
.start_layer(galaxyui::ClipBounds::BoundedBy(clip_bounds));
.start_layer(galaxyui_core::ClipBounds::BoundedBy(clip_bounds));
// Save the clipped content layer z-index for hover detection.
self.content_z_index = Some(ctx.scene.z_index());
@@ -1236,7 +1251,7 @@ impl<V: EditorView> NewScrollableElement for RichTextElement<V> {
})
}
fn scroll(&mut self, delta: galaxyui::units::Pixels, axis: Axis, ctx: &mut EventContext) {
fn scroll(&mut self, delta: galaxyui_core::units::Pixels, axis: Axis, ctx: &mut EventContext) {
if let Some(action) = V::Action::scroll(delta, axis) {
ctx.dispatch_typed_action(action);
}
@@ -1257,7 +1272,7 @@ impl<V: EditorView> ScrollableElement for RichTextElement<V> {
Some(self.vertical_scroll_data(app))
}
fn scroll(&mut self, delta: galaxyui::units::Pixels, ctx: &mut EventContext) {
fn scroll(&mut self, delta: galaxyui_core::units::Pixels, ctx: &mut EventContext) {
if let Some(action) = V::Action::scroll(delta, Axis::Vertical) {
ctx.dispatch_typed_action(action);
}
@@ -1,21 +1,17 @@
use std::sync::Arc;
use crate::{
content::text::BufferBlockStyle,
extract_block,
render::{
layout::TextLayout,
model::{BlockItem, RenderState, viewport::ViewportItem},
},
};
use galaxyui::elements::ListIndentLevel;
use galaxyui::{geometry::vector::vec2f, text_layout::TextFrame};
use galaxyui_core::elements::ListIndentLevel;
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::text_layout::TextFrame;
use super::{
RenderableBlock,
paint::RenderContext,
placeholder::{self, BlockPlaceholder},
};
use super::RenderableBlock;
use super::paint::RenderContext;
use super::placeholder::{self, BlockPlaceholder};
use crate::content::text::BufferBlockStyle;
use crate::extract_block;
use crate::render::layout::TextLayout;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
pub struct RenderableOrderedListItem {
viewport_item: ViewportItem,
@@ -44,8 +40,8 @@ impl RenderableBlock for RenderableOrderedListItem {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
let text_layout = TextLayout::from_layout_context(ctx, app, model);
let block_style = BufferBlockStyle::OrderedList {
@@ -75,7 +71,12 @@ impl RenderableBlock for RenderableOrderedListItem {
});
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
let content = model.content();
let paragraph = extract_block!(self.viewport_item, content, (block, BlockItem::OrderedList{ paragraph: inner, ..}) => block.ordered_list(inner));
+13 -21
View File
@@ -2,29 +2,21 @@
use std::ops::Range;
use galaxy_core::ui::appearance::DEFAULT_UI_FONT_SIZE;
use galaxyui::{
PaintContext,
elements::{CornerRadius, Point, Radius},
geometry::{
rect::RectF,
vector::{Vector2F, vec2f},
},
text_layout::{Line, PaintStyleOverride, TextFrame},
};
use crate::{
editor::TextDecoration,
render::{
layout::line_height,
model::{
Decoration, Paragraph, ParagraphStyles, Positioned, RenderState, RichTextStyles,
saved_positions::SavedPositions,
},
},
};
use string_offset::CharOffset;
use vim::vim::VimMode;
use galaxy_core::ui::appearance::DEFAULT_UI_FONT_SIZE;
use galaxyui_core::PaintContext;
use galaxyui_core::elements::{CornerRadius, Point, Radius};
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use galaxyui_core::text_layout::{Line, PaintStyleOverride, TextFrame};
use crate::editor::TextDecoration;
use crate::render::layout::line_height;
use crate::render::model::saved_positions::SavedPositions;
use crate::render::model::{
Decoration, Paragraph, ParagraphStyles, Positioned, RenderState, RichTextStyles,
};
const DEFAULT_BLOCK_CURSOR_WIDTH: f32 = 8.;
+15 -14
View File
@@ -1,14 +1,10 @@
use crate::{
content::text::BufferBlockStyle,
extract_block,
render::model::{BlockItem, RenderState, viewport::ViewportItem},
};
use super::{
RenderableBlock,
paint::RenderContext,
placeholder::{self, BlockPlaceholder},
};
use super::RenderableBlock;
use super::paint::RenderContext;
use super::placeholder::{self, BlockPlaceholder};
use crate::content::text::BufferBlockStyle;
use crate::extract_block;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
/// The placeholder text to show in empty plain-text blocks.
pub(super) const PARAGRAPH_PLACEHOLDER_TEXT: &str =
@@ -47,8 +43,8 @@ impl RenderableBlock for RenderableParagraph {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
self.placeholder
.layout(&self.viewport_item, model, ctx, app, |_| {
@@ -59,7 +55,12 @@ impl RenderableBlock for RenderableParagraph {
});
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
let content = model.content();
let paragraph = extract_block!(self.viewport_item, content, (block, BlockItem::Paragraph(inner)) => block.paragraph(inner));
@@ -1,21 +1,15 @@
use std::sync::Arc;
use galaxyui::{
AppContext, LayoutContext,
geometry::vector::{Vector2F, vec2f},
text_layout::Line,
};
use crate::{
content::text::BufferBlockStyle,
render::{
element::paint::CursorDisplayType,
layout::{TextLayout, line_height},
model::{BlockItem, RenderState, viewport::ViewportItem},
},
};
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use galaxyui_core::text_layout::Line;
use galaxyui_core::{AppContext, LayoutContext};
use super::{CursorData, RenderContext};
use crate::content::text::BufferBlockStyle;
use crate::render::element::paint::CursorDisplayType;
use crate::render::layout::{TextLayout, line_height};
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
/// Ghost/placeholder text that's shown in an empty block to provide context.
pub struct BlockPlaceholder {
@@ -1,19 +1,13 @@
use galaxyui::{
AppContext, Element, SizeConstraint,
elements::{Border, CornerRadius, Empty, Radius},
geometry::vector::vec2f,
};
use crate::{
editor::RunnableCommandModel,
extract_block,
render::{
BLOCK_FOOTER_HEIGHT,
model::{BlockItem, RenderState, viewport::ViewportItem},
},
};
use galaxyui_core::elements::{Border, CornerRadius, Empty, Radius};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::{AppContext, Element, SizeConstraint};
use super::{RenderContext, RenderableBlock};
use crate::editor::RunnableCommandModel;
use crate::extract_block;
use crate::render::BLOCK_FOOTER_HEIGHT;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
/// [`RenderableBlock`] implementation for runnable command blocks.
pub struct RenderableRunnableCommand {
@@ -51,7 +45,7 @@ impl RenderableBlock for RenderableRunnableCommand {
fn layout(
&mut self,
_model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &AppContext,
) {
self.footer.layout(
@@ -66,7 +60,7 @@ impl RenderableBlock for RenderableRunnableCommand {
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, app: &AppContext) {
let content = model.content();
let code_block = extract_block!(self.viewport_item, content, (block, BlockItem::RunnableCodeBlock{code_block_type: _, paragraph_block}) => block.code_block(paragraph_block));
let code_block = extract_block!(self.viewport_item, content, (block, BlockItem::RunnableCodeBlock{paragraph_block, ..}) => block.code_block(paragraph_block));
let styles = model.styles();
let code_style = &styles.code_text;
@@ -94,7 +88,7 @@ impl RenderableBlock for RenderableRunnableCommand {
// `RichTextElement::content_z_index` for context.
ctx.paint
.scene
.start_layer(galaxyui::ClipBounds::ActiveLayer);
.start_layer(galaxyui_core::ClipBounds::ActiveLayer);
// Position the block footer right below the content area, flush with its right-hand edge.
// This gives the footer some padding relative to the visible area with a background.
@@ -109,15 +103,19 @@ impl RenderableBlock for RenderableRunnableCommand {
ctx.paint.scene.stop_layer();
}
fn after_layout(&mut self, ctx: &mut galaxyui::AfterLayoutContext, app: &galaxyui::AppContext) {
fn after_layout(
&mut self,
ctx: &mut galaxyui_core::AfterLayoutContext,
app: &galaxyui_core::AppContext,
) {
self.footer.after_layout(ctx, app);
}
fn dispatch_event(
&mut self,
_model: &crate::render::model::RenderState,
event: &galaxyui::event::DispatchedEvent,
ctx: &mut galaxyui::EventContext,
event: &galaxyui_core::event::DispatchedEvent,
ctx: &mut galaxyui_core::EventContext,
app: &AppContext,
) -> bool {
self.footer.dispatch_event(event, ctx, app)
+17 -26
View File
@@ -1,32 +1,23 @@
use galaxyui::{
AppContext, ClipBounds, Event, EventContext,
elements::{
Axis, CornerRadius, DEFAULT_SCROLL_WHEEL_PIXELS_PER_LINE, Radius, ScrollData,
ScrollbarAppearance, ScrollbarGeometry, ScrollbarWidth, compute_scrollbar_geometry,
project_scroll_delta_by_sensitivity, scroll_delta_for_pointer_movement,
},
event::DispatchedEvent,
geometry::{
rect::RectF,
vector::{Vector2F, vec2f},
},
units::{IntoPixels, Pixels},
};
use std::ops::Range;
use string_offset::CharOffset;
use galaxyui_core::elements::{
Axis, CornerRadius, DEFAULT_SCROLL_WHEEL_PIXELS_PER_LINE, Radius, ScrollData,
ScrollbarAppearance, ScrollbarGeometry, ScrollbarWidth, compute_scrollbar_geometry,
project_scroll_delta_by_sensitivity, scroll_delta_for_pointer_movement,
};
use galaxyui_core::event::DispatchedEvent;
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use galaxyui_core::units::{IntoPixels, Pixels};
use galaxyui_core::{AppContext, ClipBounds, Event, EventContext};
use super::paint::{CursorData, CursorDisplayType};
use super::{RenderContext, RenderableBlock};
use crate::extract_block;
use crate::render::model::table_offset_map::CellAtOffset;
use crate::{
extract_block,
render::model::{
BlockItem, LaidOutTable, RenderState, RenderedSelection, TableStyle, viewport::ViewportItem,
},
};
use super::{
RenderContext, RenderableBlock,
paint::{CursorData, CursorDisplayType},
};
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, LaidOutTable, RenderState, RenderedSelection, TableStyle};
const TABLE_BORDER_WIDTH: f32 = 1.0;
const TABLE_SCROLL_SENSITIVITY: f32 = 1.0;
@@ -121,7 +112,7 @@ impl RenderableBlock for RenderableTable {
&self.viewport_item
}
fn layout(&mut self, _: &RenderState, _: &mut galaxyui::LayoutContext, _: &AppContext) {}
fn layout(&mut self, _: &RenderState, _: &mut galaxyui_core::LayoutContext, _: &AppContext) {}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &AppContext) {
let content = model.content();
+21 -24
View File
@@ -1,27 +1,24 @@
use galaxyui::{
elements::{Axis, scroll_delta_for_pointer_movement},
fonts::FamilyId,
geometry::{rect::RectF, vector::vec2f},
text_layout::TextFrame,
units::{IntoPixels, Pixels},
};
use pathfinder_color::ColorU;
use std::{cell::Cell, sync::Arc};
use string_offset::CharOffset;
use std::cell::Cell;
use std::sync::Arc;
use crate::{
content::text::{FormattedTable, table_cell_offset_maps},
render::{
element::table::{
model_table_layout_report, row_geometry_from_layout_report,
table_cursor_relative_offset, table_horizontal_scroll_delta, table_scroll_data,
table_scrollbar, table_selection_relative_range,
},
model::{
BlockSpacing, CellLayout, LaidOutTable, RenderedSelection, TableBlockConfig,
TableStyle, table_offset_map::TableOffsetMap,
},
},
use pathfinder_color::ColorU;
use string_offset::CharOffset;
use galaxyui_core::elements::{Axis, scroll_delta_for_pointer_movement};
use galaxyui_core::fonts::FamilyId;
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::text_layout::TextFrame;
use galaxyui_core::units::{IntoPixels, Pixels};
use crate::content::text::{FormattedTable, table_cell_offset_maps};
use crate::render::element::table::{
model_table_layout_report, row_geometry_from_layout_report, table_cursor_relative_offset,
table_horizontal_scroll_delta, table_scroll_data, table_scrollbar,
table_selection_relative_range,
};
use crate::render::model::table_offset_map::TableOffsetMap;
use crate::render::model::{
BlockSpacing, CellLayout, LaidOutTable, RenderedSelection, TableBlockConfig, TableStyle,
};
fn test_laid_out_table() -> LaidOutTable {
@@ -214,7 +211,7 @@ fn cells_in_range_entire_table() {
}
fn single_line_cell_layout(char_count: usize, line_height: f32, line_width: f32) -> CellLayout {
use galaxyui::text_layout::CaretPosition;
use galaxyui_core::text_layout::CaretPosition;
let mut carets = Vec::with_capacity(char_count);
let char_width = if char_count > 0 {
line_width / char_count as f32
+25 -26
View File
@@ -1,26 +1,20 @@
use crate::{
content::text::BufferBlockStyle,
editor::EditorView,
extract_block,
render::model::{BlockItem, RenderState, RichTextStyles, bounds, viewport::ViewportItem},
};
use galaxyui::elements::ListIndentLevel;
use galaxyui::{
AppContext, Element, SizeConstraint, WeakViewHandle,
elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, Hoverable, Icon, MouseStateHandle,
Radius, Rect,
},
geometry::vector::vec2f,
platform::Cursor,
};
use pathfinder_color::ColorU;
use super::{
RenderableBlock, RichTextAction,
paint::RenderContext,
placeholder::{self, BlockPlaceholder},
use galaxyui_core::elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, Hoverable, Icon, ListIndentLevel,
MouseStateHandle, Radius, Rect,
};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::platform::Cursor;
use galaxyui_core::{AppContext, Element, SizeConstraint, WeakViewHandle};
use super::paint::RenderContext;
use super::placeholder::{self, BlockPlaceholder};
use super::{RenderableBlock, RichTextAction};
use crate::content::text::BufferBlockStyle;
use crate::editor::EditorView;
use crate::extract_block;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState, RichTextStyles, bounds};
// Minimum size constraint for the checkbox point. If the size is smaller than the constraint,
// the svg won't render.
@@ -112,8 +106,8 @@ impl RenderableBlock for RenderableTaskList {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
self.task_list_icon.layout(
SizeConstraint::strict(vec2f(self.icon_size, self.icon_size)),
@@ -142,7 +136,12 @@ impl RenderableBlock for RenderableTaskList {
})
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
app: &galaxyui_core::AppContext,
) {
let content = model.content();
let task_list = extract_block!(self.viewport_item, content, (block, BlockItem::TaskList{ paragraph: inner, ..}) => block.task_list(inner));
let text_styling = &model.styles().base_text;
@@ -174,8 +173,8 @@ impl RenderableBlock for RenderableTaskList {
fn dispatch_event(
&mut self,
_model: &crate::render::model::RenderState,
event: &galaxyui::event::DispatchedEvent,
ctx: &mut galaxyui::EventContext,
event: &galaxyui_core::event::DispatchedEvent,
ctx: &mut galaxyui_core::EventContext,
app: &AppContext,
) -> bool {
self.task_list_icon.dispatch_event(event, ctx, app)
@@ -1,8 +1,9 @@
use galaxy_core::ui::theme::Fill;
use crate::render::model::{BlockItem, Decoration, RenderState, viewport::ViewportItem};
use super::{RenderableBlock, paint::RenderContext};
use super::RenderableBlock;
use super::paint::RenderContext;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, Decoration, RenderState};
pub struct RenderableTemporaryBlock {
viewport_item: ViewportItem,
@@ -36,12 +37,17 @@ impl RenderableBlock for RenderableTemporaryBlock {
fn layout(
&mut self,
_model: &RenderState,
_ctx: &mut galaxyui::LayoutContext,
_app: &galaxyui::AppContext,
_ctx: &mut galaxyui_core::LayoutContext,
_app: &galaxyui_core::AppContext,
) {
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
// We cannot use `extract_block` macro here since we need to locate the viewport item by content height instead of charoffset
// (temporary block has an offset of zero).
let content = model.content();
+13 -9
View File
@@ -1,9 +1,8 @@
use crate::{
extract_block,
render::model::{BlockItem, RenderState, viewport::ViewportItem},
};
use super::{RenderableBlock, paint::RenderContext};
use super::RenderableBlock;
use super::paint::RenderContext;
use crate::extract_block;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState};
pub struct RenderableTextBlock {
viewport_item: ViewportItem,
@@ -23,12 +22,17 @@ impl RenderableBlock for RenderableTextBlock {
fn layout(
&mut self,
_model: &RenderState,
_ctx: &mut galaxyui::LayoutContext,
_app: &galaxyui::AppContext,
_ctx: &mut galaxyui_core::LayoutContext,
_app: &galaxyui_core::AppContext,
) {
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, _app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
_app: &galaxyui_core::AppContext,
) {
let content = model.content();
let text_block = extract_block!(
self.viewport_item,
@@ -1,20 +1,14 @@
use crate::{
content::text::BufferBlockStyle,
extract_block,
render::model::{BlockItem, RenderState, RichTextStyles, bounds, viewport::ViewportItem},
};
use galaxyui::elements::ListIndentLevel;
use galaxyui::{
Element, SizeConstraint,
elements::{Border, CornerRadius, Radius, Rect},
geometry::vector::vec2f,
};
use galaxyui_core::elements::{Border, CornerRadius, ListIndentLevel, Radius, Rect};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::{Element, SizeConstraint};
use super::{
RenderableBlock,
paint::RenderContext,
placeholder::{self, BlockPlaceholder},
};
use super::RenderableBlock;
use super::paint::RenderContext;
use super::placeholder::{self, BlockPlaceholder};
use crate::content::text::BufferBlockStyle;
use crate::extract_block;
use crate::render::model::viewport::ViewportItem;
use crate::render::model::{BlockItem, RenderState, RichTextStyles, bounds};
// Minimum size constraint for the bullet point. If the size is smaller than the constraint,
// the svg won't render.
@@ -67,8 +61,8 @@ impl RenderableBlock for RenderableBulletList {
fn layout(
&mut self,
model: &RenderState,
ctx: &mut galaxyui::LayoutContext,
app: &galaxyui::AppContext,
ctx: &mut galaxyui_core::LayoutContext,
app: &galaxyui_core::AppContext,
) {
self.bullet_point.layout(
SizeConstraint::strict(vec2f(self.bullet_size, self.bullet_size)),
@@ -88,7 +82,12 @@ impl RenderableBlock for RenderableBulletList {
})
}
fn paint(&mut self, model: &RenderState, ctx: &mut RenderContext, app: &galaxyui::AppContext) {
fn paint(
&mut self,
model: &RenderState,
ctx: &mut RenderContext,
app: &galaxyui_core::AppContext,
) {
let content = model.content();
let unordered_list = extract_block!(self.viewport_item, content, (block, BlockItem::UnorderedList{ paragraph: inner, ..}) => block.unordered_list(inner));
+10 -10
View File
@@ -1,22 +1,22 @@
//! Shared text-layout utilities needed throughout the editor implementation.
#[cfg(test)]
use markdown_parser::FormattedTextInline;
use std::ops::Range;
use std::sync::Arc;
use crate::content::text::{BufferBlockStyle, TextStylesWithMetadata};
use galaxyui::fonts::TextLayoutSystem;
#[cfg(test)]
use galaxyui::fonts::{Style, Weight};
use galaxyui::text_layout::{
ClipConfig, LayoutCache, Line, StyleAndFont, TextAlignment, TextBorder, TextStyle,
use markdown_parser::FormattedTextInline;
use galaxyui_core::color::ColorU;
use galaxyui_core::fonts::TextLayoutSystem;
#[cfg(test)]
use galaxyui_core::fonts::{Style, Weight};
use galaxyui_core::text_layout::{
ClipConfig, LayoutCache, Line, StyleAndFont, TextAlignment, TextBorder, TextFrame, TextStyle,
};
use galaxyui::units::{IntoPixels, Pixels};
use galaxyui::{AppContext, LayoutContext};
use galaxyui::{color::ColorU, text_layout::TextFrame};
use galaxyui_core::units::{IntoPixels, Pixels};
use galaxyui_core::{AppContext, LayoutContext};
use super::model::{BlockSpacing, ParagraphStyles, RenderState, RichTextStyles};
use crate::content::text::{BufferBlockStyle, TextStylesWithMetadata};
const HYPERLINK_UNDERLINE_COLOR: u32 = 0x7aa6daff;
+96 -102
View File
@@ -1,24 +1,18 @@
//! End-to-end editor tests.
use galaxy_core::features::FeatureFlag;
use galaxyui::{App, ModelHandle, ReadModel};
use crate::content::{
buffer::{
AutoScrollBehavior, Buffer, BufferEditAction, BufferEvent, BufferSelectAction, EditOrigin,
InitialBufferState,
},
selection_model::BufferSelectionModel,
text::{BlockType, BufferBlockItem, IndentBehavior, TextStyles},
version::BufferVersion,
};
use string_offset::CharOffset;
use galaxy_core::features::FeatureFlag;
use galaxyui_core::{App, ModelHandle, ReadModel};
use super::model::{
BlockItem, RenderEvent, RenderState,
test_utils::{TEST_STYLES, init_logging},
use super::model::test_utils::{TEST_STYLES, init_logging};
use super::model::{BlockItem, RenderEvent, RenderState};
use crate::content::buffer::{
AutoScrollBehavior, Buffer, BufferEditAction, BufferEvent, BufferSelectAction, EditOrigin,
InitialBufferState, ShouldAutoscroll,
};
use crate::content::buffer::ShouldAutoscroll;
use crate::content::selection_model::BufferSelectionModel;
use crate::content::text::{BlockType, BufferBlockItem, IndentBehavior, TextStyles};
use crate::content::version::BufferVersion;
#[test]
fn test_simple_edit() {
@@ -44,7 +38,7 @@ fn test_simple_edit() {
&app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (2 characters, 1 lines, 32.00px tall)
Paragraph (2 characters, 1 lines, 24.00px tall)
"#,
);
});
@@ -94,57 +88,57 @@ zzzzzzzzzzzzzzzzzzzzzzzzzz"#,
&app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (2 characters, 1 lines, 32.00px tall)
-------- 32.00px / 2 characters --------
Paragraph (3 characters, 1 lines, 32.00px tall)
-------- 64.00px / 5 characters --------
Paragraph (4 characters, 1 lines, 32.00px tall)
-------- 96.00px / 9 characters --------
Paragraph (5 characters, 1 lines, 32.00px tall)
-------- 128.00px / 14 characters --------
Paragraph (6 characters, 1 lines, 32.00px tall)
-------- 160.00px / 20 characters --------
Paragraph (7 characters, 1 lines, 32.00px tall)
-------- 192.00px / 27 characters --------
Paragraph (8 characters, 1 lines, 32.00px tall)
-------- 224.00px / 35 characters --------
Paragraph (9 characters, 1 lines, 32.00px tall)
-------- 256.00px / 44 characters --------
Paragraph (10 characters, 1 lines, 32.00px tall)
-------- 288.00px / 54 characters --------
Paragraph (11 characters, 1 lines, 32.00px tall)
-------- 320.00px / 65 characters --------
Paragraph (12 characters, 1 lines, 32.00px tall)
-------- 352.00px / 77 characters --------
Paragraph (13 characters, 1 lines, 32.00px tall)
-------- 384.00px / 90 characters --------
Paragraph (14 characters, 1 lines, 32.00px tall)
-------- 416.00px / 104 characters --------
Paragraph (15 characters, 1 lines, 32.00px tall)
-------- 448.00px / 119 characters --------
Paragraph (16 characters, 1 lines, 32.00px tall)
-------- 480.00px / 135 characters --------
Paragraph (17 characters, 1 lines, 32.00px tall)
-------- 512.00px / 152 characters --------
Paragraph (18 characters, 1 lines, 32.00px tall)
-------- 544.00px / 170 characters --------
Paragraph (19 characters, 1 lines, 32.00px tall)
-------- 576.00px / 189 characters --------
Paragraph (20 characters, 1 lines, 32.00px tall)
-------- 608.00px / 209 characters --------
Paragraph (21 characters, 1 lines, 32.00px tall)
-------- 640.00px / 230 characters --------
Paragraph (22 characters, 1 lines, 32.00px tall)
-------- 672.00px / 252 characters --------
Paragraph (23 characters, 1 lines, 32.00px tall)
-------- 704.00px / 275 characters --------
Paragraph (24 characters, 1 lines, 32.00px tall)
-------- 736.00px / 299 characters --------
Paragraph (25 characters, 1 lines, 32.00px tall)
-------- 768.00px / 324 characters --------
Paragraph (26 characters, 1 lines, 32.00px tall)
-------- 800.00px / 350 characters --------
Paragraph (27 characters, 1 lines, 32.00px tall)
Paragraph (2 characters, 1 lines, 24.00px tall)
-------- 24.00px / 2 characters --------
Paragraph (3 characters, 1 lines, 24.00px tall)
-------- 48.00px / 5 characters --------
Paragraph (4 characters, 1 lines, 24.00px tall)
-------- 72.00px / 9 characters --------
Paragraph (5 characters, 1 lines, 24.00px tall)
-------- 96.00px / 14 characters --------
Paragraph (6 characters, 1 lines, 24.00px tall)
-------- 120.00px / 20 characters --------
Paragraph (7 characters, 1 lines, 24.00px tall)
-------- 144.00px / 27 characters --------
Paragraph (8 characters, 1 lines, 24.00px tall)
-------- 168.00px / 35 characters --------
Paragraph (9 characters, 1 lines, 24.00px tall)
-------- 192.00px / 44 characters --------
Paragraph (10 characters, 1 lines, 24.00px tall)
-------- 216.00px / 54 characters --------
Paragraph (11 characters, 1 lines, 24.00px tall)
-------- 240.00px / 65 characters --------
Paragraph (12 characters, 1 lines, 24.00px tall)
-------- 264.00px / 77 characters --------
Paragraph (13 characters, 1 lines, 24.00px tall)
-------- 288.00px / 90 characters --------
Paragraph (14 characters, 1 lines, 24.00px tall)
-------- 312.00px / 104 characters --------
Paragraph (15 characters, 1 lines, 24.00px tall)
-------- 336.00px / 119 characters --------
Paragraph (16 characters, 1 lines, 24.00px tall)
-------- 360.00px / 135 characters --------
Paragraph (17 characters, 1 lines, 24.00px tall)
-------- 384.00px / 152 characters --------
Paragraph (18 characters, 1 lines, 24.00px tall)
-------- 408.00px / 170 characters --------
Paragraph (19 characters, 1 lines, 24.00px tall)
-------- 432.00px / 189 characters --------
Paragraph (20 characters, 1 lines, 24.00px tall)
-------- 456.00px / 209 characters --------
Paragraph (21 characters, 1 lines, 24.00px tall)
-------- 480.00px / 230 characters --------
Paragraph (22 characters, 1 lines, 24.00px tall)
-------- 504.00px / 252 characters --------
Paragraph (23 characters, 1 lines, 24.00px tall)
-------- 528.00px / 275 characters --------
Paragraph (24 characters, 1 lines, 24.00px tall)
-------- 552.00px / 299 characters --------
Paragraph (25 characters, 1 lines, 24.00px tall)
-------- 576.00px / 324 characters --------
Paragraph (26 characters, 1 lines, 24.00px tall)
-------- 600.00px / 350 characters --------
Paragraph (27 characters, 1 lines, 24.00px tall)
"#,
);
});
@@ -173,13 +167,13 @@ fn test_enter_before_horizontal_rule() {
app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (11 characters, 1 lines, 32.00px tall)
-------- 32.00px / 11 characters --------
Paragraph (1 characters, 1 lines, 32.00px tall)
-------- 64.00px / 12 characters --------
Horizontal Rule (1 characters, 1 lines, 18.00px tall)
-------- 82.00px / 13 characters --------
Paragraph (12 characters, 1 lines, 32.00px tall)
Paragraph (11 characters, 1 lines, 24.00px tall)
-------- 24.00px / 11 characters --------
Paragraph (1 characters, 1 lines, 24.00px tall)
-------- 48.00px / 12 characters --------
Horizontal Rule (1 characters, 1 lines, 10.00px tall)
-------- 58.00px / 13 characters --------
Paragraph (12 characters, 1 lines, 24.00px tall)
"#,
);
})
@@ -208,13 +202,13 @@ fn test_enter_after_horizontal_rule() {
app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (11 characters, 1 lines, 32.00px tall)
-------- 32.00px / 11 characters --------
Horizontal Rule (1 characters, 1 lines, 18.00px tall)
-------- 50.00px / 12 characters --------
Paragraph (1 characters, 1 lines, 32.00px tall)
-------- 82.00px / 13 characters --------
Paragraph (12 characters, 1 lines, 32.00px tall)
Paragraph (11 characters, 1 lines, 24.00px tall)
-------- 24.00px / 11 characters --------
Horizontal Rule (1 characters, 1 lines, 10.00px tall)
-------- 34.00px / 12 characters --------
Paragraph (1 characters, 1 lines, 24.00px tall)
-------- 58.00px / 13 characters --------
Paragraph (12 characters, 1 lines, 24.00px tall)
"#,
);
})
@@ -244,13 +238,13 @@ fn test_edit_at_horizontal_rule_end() {
app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (11 characters, 1 lines, 32.00px tall)
-------- 32.00px / 11 characters --------
Horizontal Rule (1 characters, 1 lines, 18.00px tall)
-------- 50.00px / 12 characters --------
Paragraph (2 characters, 1 lines, 32.00px tall)
-------- 82.00px / 14 characters --------
Paragraph (12 characters, 1 lines, 32.00px tall)
Paragraph (11 characters, 1 lines, 24.00px tall)
-------- 24.00px / 11 characters --------
Horizontal Rule (1 characters, 1 lines, 10.00px tall)
-------- 34.00px / 12 characters --------
Paragraph (2 characters, 1 lines, 24.00px tall)
-------- 58.00px / 14 characters --------
Paragraph (12 characters, 1 lines, 24.00px tall)
"#,
);
})
@@ -286,9 +280,9 @@ fn test_edit_after_style() {
app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (18 characters, 1 lines, 32.00px tall)
-------- 32.00px / 18 characters --------
Paragraph (9 characters, 1 lines, 32.00px tall)
Paragraph (18 characters, 1 lines, 24.00px tall)
-------- 24.00px / 18 characters --------
Paragraph (9 characters, 1 lines, 24.00px tall)
"#,
);
})
@@ -323,7 +317,7 @@ Task List @ 1 [X] (2 characters, 1 lines, 18.00px tall)
-------- 18.00px / 2 characters --------
Task List @ 1 [ ] (2 characters, 1 lines, 18.00px tall)
-------- 36.00px / 4 characters --------
Trailing Newline (1 characters, 1 lines, 32.00px tall)
Trailing Newline (1 characters, 1 lines, 24.00px tall)
"#,
);
@@ -344,7 +338,7 @@ Task List @ 1 [ ] (2 characters, 1 lines, 18.00px tall)
-------- 54.00px / 6 characters --------
Task List @ 1 [ ] (2 characters, 1 lines, 18.00px tall)
-------- 72.00px / 8 characters --------
Trailing Newline (1 characters, 1 lines, 32.00px tall)
Trailing Newline (1 characters, 1 lines, 24.00px tall)
"#,
)
});
@@ -377,11 +371,11 @@ fn test_convert_first_line() {
app,
r#"
-------- 0.00px / 0 characters --------
Horizontal Rule (1 characters, 1 lines, 18.00px tall)
-------- 18.00px / 1 characters --------
Horizontal Rule (1 characters, 1 lines, 10.00px tall)
-------- 10.00px / 1 characters --------
Code Block - Shell (5 characters, 1 lines, 84.00px tall)
-------- 102.00px / 6 characters --------
Trailing Newline (1 characters, 1 lines, 32.00px tall)
-------- 94.00px / 6 characters --------
Trailing Newline (1 characters, 1 lines, 24.00px tall)
"#,
);
@@ -394,11 +388,11 @@ Trailing Newline (1 characters, 1 lines, 32.00px tall)
app,
r#"
-------- 0.00px / 0 characters --------
Paragraph (3 characters, 1 lines, 32.00px tall)
-------- 32.00px / 3 characters --------
Paragraph (3 characters, 1 lines, 24.00px tall)
-------- 24.00px / 3 characters --------
Code Block - Shell (5 characters, 1 lines, 84.00px tall)
-------- 116.00px / 8 characters --------
Trailing Newline (1 characters, 1 lines, 32.00px tall)
-------- 108.00px / 8 characters --------
Trailing Newline (1 characters, 1 lines, 24.00px tall)
"#,
)
});
+3 -7
View File
@@ -9,13 +9,9 @@
//! * The **reserved box** is the rectangle containing a block's content, padding, borders, and
//! margin - all space reserved for the block.
use galaxyui::{
geometry::{
rect::RectF,
vector::{Vector2F, vec2f},
},
units::Pixels,
};
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use galaxyui_core::units::Pixels;
use super::BlockSpacing;
+3 -3
View File
@@ -2,13 +2,13 @@
use galaxyui::units::{IntoPixels, Pixels};
use num_traits::SaturatingSub;
use sum_tree::SeekBias;
use string_offset::CharOffset;
use sum_tree::SeekBias;
use galaxyui_core::units::{IntoPixels, Pixels};
use super::positioned::{Positioned, PositionedCursor};
use super::{
BlockItem, Height, HitTestBlockType, LayoutSummary, ParagraphBlock, RenderState, bounds,
positioned::{Positioned, PositionedCursor},
};
#[cfg(test)]
@@ -1,25 +1,25 @@
use crate::content::text::{FormattedTable, table_cell_offset_maps};
use crate::{
content::text::{BufferBlockStyle, CodeBlockType},
render::model::{
BlockItem, COMMAND_SPACING, CellLayout, ImageBlockConfig, LaidOutTable, Location,
ParagraphBlock, RenderState, TableBlockConfig, TableStyle,
location::{HitTestBlockType, HitTestOptions, WrapDirection},
table_offset_map,
test_utils::{
TEST_STYLES, laid_out_paragraph, laid_out_unordered_lists, layout_paragraphs,
},
},
};
use pathfinder_color::ColorU;
use std::{cell::Cell, sync::Arc};
use string_offset::CharOffset;
use std::cell::Cell;
use std::sync::Arc;
use galaxyui::assets::asset_cache::AssetSource;
use galaxyui::fonts::FamilyId;
use galaxyui::text_layout::{CaretPosition, TextFrame};
use galaxyui::units::IntoPixels;
use pathfinder_color::ColorU;
use string_offset::CharOffset;
use sum_tree::SumTree;
use galaxyui_core::assets::asset_cache::AssetSource;
use galaxyui_core::fonts::FamilyId;
use galaxyui_core::text_layout::{CaretPosition, TextFrame};
use galaxyui_core::units::IntoPixels;
use crate::content::text::{
BufferBlockStyle, CodeBlockType, FormattedTable, table_cell_offset_maps,
};
use crate::render::model::location::{HitTestBlockType, HitTestOptions, WrapDirection};
use crate::render::model::test_utils::{
TEST_STYLES, laid_out_paragraph, laid_out_unordered_lists, layout_paragraphs,
};
use crate::render::model::{
BlockItem, COMMAND_SPACING, CellLayout, ImageBlockConfig, LaidOutTable, Location,
ParagraphBlock, RenderState, TableBlockConfig, TableStyle, table_offset_map,
};
fn test_table_layout() -> LaidOutTable {
let source = "aaa\tbbb\nccc\tddd\n";
@@ -493,10 +493,10 @@ fn test_hit_scrolled() {
&Default::default()
),
Location::Text {
char_offset: 15.into(),
clamped: true,
wrap_direction: WrapDirection::Up,
block_start: 8.into(),
char_offset: 19.into(),
clamped: false,
wrap_direction: WrapDirection::Down,
block_start: 16.into(),
link: None,
}
);
@@ -510,10 +510,10 @@ fn test_hit_scrolled() {
&Default::default()
),
Location::Text {
char_offset: 16.into(),
clamped: false,
char_offset: 21.into(),
clamped: true,
wrap_direction: WrapDirection::Down,
block_start: 16.into(),
block_start: 21.into(),
link: None,
}
);
@@ -580,23 +580,24 @@ fn test_hit_code_block() {
width - COMMAND_SPACING.x_axis_offset().as_f32(),
)),
code_block_type: Default::default(),
pending_mermaid_asset: None,
},
]);
model.set_content(tree);
// Blocks by height:
// * 0-32: First paragraph
// * 32-56: Margin above code block
// * 56-66: First line of code
// * 66-76: Second line of code
// * 76-92: Margin below code block
// * 0-24: First paragraph
// * 24-48: Margin above code block
// * 48-58: First line of code
// * 58-68: Second line of code
// * 68-84: Margin below code block
// The code block is inset by 16px.
// Hits within the code block should have the right start location.
assert_eq!(
model.render_coordinates_to_location(
30.0.into_pixels(),
70.0.into_pixels(),
62.0.into_pixels(),
&Default::default()
),
Location::Text {
@@ -614,7 +615,7 @@ fn test_hit_code_block() {
assert_eq!(
model.render_coordinates_to_location(
10.0.into_pixels(),
50.0.into_pixels(),
40.0.into_pixels(),
&Default::default()
),
Location::Block {
@@ -628,7 +629,7 @@ fn test_hit_code_block() {
assert_eq!(
model.render_coordinates_to_location(
8.0.into_pixels(),
60.0.into_pixels(),
52.0.into_pixels(),
&Default::default()
),
Location::Text {
@@ -642,7 +643,7 @@ fn test_hit_code_block() {
assert_eq!(
model.render_coordinates_to_location(
90.0.into_pixels(),
60.0.into_pixels(),
52.0.into_pixels(),
&Default::default()
),
Location::Text {
@@ -658,7 +659,7 @@ fn test_hit_code_block() {
assert_eq!(
model.render_coordinates_to_location(
(-4.).into_pixels(),
60.0.into_pixels(),
52.0.into_pixels(),
&Default::default()
),
Location::Text {
@@ -672,7 +673,7 @@ fn test_hit_code_block() {
assert_eq!(
model.render_coordinates_to_location(
1000.0.into_pixels(),
60.0.into_pixels(),
52.0.into_pixels(),
&Default::default()
),
Location::Text {
@@ -688,7 +689,7 @@ fn test_hit_code_block() {
assert_eq!(
model.render_coordinates_to_location(
27.0.into_pixels(),
50.0.into_pixels(),
40.0.into_pixels(),
&HitTestOptions {
force_text_selection: true
}
File diff suppressed because it is too large Load Diff
+337 -98
View File
@@ -1,80 +1,75 @@
use galaxyui::assets::asset_cache::AssetSource;
use galaxyui::{
color::ColorU,
fonts::FamilyId,
geometry::{rect::RectF, vector::vec2f},
text_layout::TextFrame,
units::{IntoPixels, Pixels},
};
use std::cell::Cell;
use std::sync::Arc;
use markdown_parser::{FormattedTextStyles, Hyperlink};
use rangemap::RangeSet;
use std::{cell::Cell, sync::Arc};
use string_offset::CharOffset;
use sum_tree::SumTree;
use vec1::{Vec1, vec1};
use galaxyui_core::assets::asset_cache::AssetSource;
use galaxyui_core::color::ColorU;
use galaxyui_core::elements::ListIndentLevel;
use galaxyui_core::fonts::FamilyId;
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::text_layout::TextFrame;
use galaxyui_core::units::{IntoPixels, Pixels};
use super::debug::Describe;
use super::test_utils::{layout_paragraph, layout_paragraphs};
use super::{
BlockItem, BlockLocation, COMMAND_SPACING, CellLayout, DEFAULT_BLOCK_SPACINGS,
HiddenBlockConfig, ImageBlockConfig, LaidOutTable, ParagraphBlock, RenderState,
TableBlockConfig, TableStyle,
debug::Describe,
table_offset_map,
test_utils::{layout_paragraph, layout_paragraphs},
TableBlockConfig, TableStyle, table_offset_map,
};
use crate::{
content::{
edit::ParsedUrl,
text::{
BufferBlockStyle, CodeBlockType, FormattedTable, FormattedTextFragment,
table_cell_offset_maps,
},
},
render::model::{
Height, LayoutSummary, LineCount, RenderedSelection, SoftWrapPoint, TEXT_SPACING,
test_utils::{TEST_STYLES, laid_out_paragraph, mock_paragraph},
},
use crate::content::edit::ParsedUrl;
use crate::content::text::{
BufferBlockStyle, CodeBlockType, FormattedTable, FormattedTextFragment, table_cell_offset_maps,
};
use crate::render::model::test_utils::{TEST_STYLES, laid_out_paragraph, mock_paragraph};
use crate::render::model::{
ColumnUnit, Height, LayoutSummary, LineCount, RenderedSelection, SoftWrapPoint, TEXT_SPACING,
};
use galaxyui::elements::ListIndentLevel;
use markdown_parser::{FormattedTextStyles, Hyperlink};
use string_offset::CharOffset;
#[test]
fn test_height() {
let mut render_state =
RenderState::new_for_test(TEST_STYLES, 10.0.into_pixels(), 10.0.into_pixels());
let mut content = SumTree::new();
// Height: 32
// Height: 24
content.push(mock_paragraph(24., 1., 1));
// Height: 56
// Height: 48
content.push(mock_paragraph(48., 1., 2));
// Height: 32
// Height: 24
content.push(mock_paragraph(24., 1., 3));
// Height: 32
// Height: 24
content.push(mock_paragraph(24., 1., 4));
// Height: 40
// Height: 32
content.push(mock_paragraph(32., 1., 5));
render_state.set_content(content);
// This includes all content plus the trailing newline marker.
assert_eq!(render_state.height(), 224.0.into_pixels());
assert_eq!(render_state.height(), 176.0.into_pixels());
let content = render_state.content.borrow();
let mut cursor = content.cursor::<Height, Height>();
// Ensure we can seek in between items for scrolling.
cursor.seek(&Height::from(64.), sum_tree::SeekBias::Left);
assert_eq!(
cursor.item().expect("Seek succeeded").height().as_f32(),
56.
48.
);
assert_eq!(cursor.start().into_pixels().as_f32(), 32.);
assert_eq!(cursor.end().into_pixels().as_f32(), 88.);
assert_eq!(cursor.start().into_pixels().as_f32(), 24.);
assert_eq!(cursor.end().into_pixels().as_f32(), 72.);
let end = cursor.slice(&Height::from(152.), sum_tree::SeekBias::Right);
assert_eq!(
end.summary(),
LayoutSummary {
content_length: 9.into(),
height: 56. + 32. + 32.,
width: (21.).into_pixels(),
lines: LineCount(3),
item_count: 3,
content_length: 14.into(),
height: 48. + 24. + 24. + 32.,
width: (17.).into_pixels(),
lines: LineCount(4),
item_count: 4,
}
);
}
@@ -138,7 +133,7 @@ fn test_width() {
render_state.set_content(content);
// This includes all content plus the trailing newline marker.
assert_eq!(render_state.width(), (45.).into_pixels());
assert_eq!(render_state.width(), (41.).into_pixels());
let content = render_state.content.borrow();
let mut cursor = content.cursor::<Height, Height>();
let end = cursor.slice(&Height::from(40.), sum_tree::SeekBias::Right);
@@ -146,8 +141,8 @@ fn test_width() {
end.summary(),
LayoutSummary {
content_length: 1.into(),
height: 32.,
width: (30.).into_pixels(),
height: 24.,
width: (26.).into_pixels(),
lines: LineCount(1),
item_count: 1,
}
@@ -160,6 +155,10 @@ fn test_soft_wrap_point() {
fn char_x(chars: usize) -> Pixels {
TEXT_SPACING.left_offset() + (chars as f32 * TEST_STYLES.base_text.font_size).into_pixels()
}
/// Wraps a Pixels value as a ColumnUnit for constructing SoftWrapPoints in Pixels mode.
fn px(p: Pixels) -> ColumnUnit {
ColumnUnit::Pixels(p)
}
let mut model =
RenderState::new_for_test(TEST_STYLES.clone(), 40.0.into_pixels(), 60.0.into_pixels());
@@ -181,82 +180,82 @@ fn test_soft_wrap_point() {
// Last point on the first softwrapped line.
assert_eq!(
model.offset_to_softwrap_point(CharOffset::from(3)),
SoftWrapPoint::new(0, char_x(3))
SoftWrapPoint::new(0, px(char_x(3)))
);
// A point slightly closer to 2 than 3 should round to 2.
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(0, char_x(2) + 4.0.into_pixels())),
model.softwrap_point_to_offset(SoftWrapPoint::new(0, px(char_x(2) + 4.0.into_pixels()))),
CharOffset::from(2)
);
// A point slightly closer to 3 than 2 should round to 3.
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(0, char_x(3) - 4.0.into_pixels())),
model.softwrap_point_to_offset(SoftWrapPoint::new(0, px(char_x(3) - 4.0.into_pixels()))),
CharOffset::from(3)
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(0, char_x(4))),
model.softwrap_point_to_offset(SoftWrapPoint::new(0, px(char_x(4)))),
CharOffset::from(4)
);
// Point on the second softwrapped line in the first paragraph.
assert_eq!(
model.offset_to_softwrap_point(CharOffset::from(7)),
SoftWrapPoint::new(1, char_x(3))
SoftWrapPoint::new(1, px(char_x(3)))
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(1, char_x(3))),
model.softwrap_point_to_offset(SoftWrapPoint::new(1, px(char_x(3)))),
CharOffset::from(7)
);
// Non-softwrapped line should work as well.
assert_eq!(
model.offset_to_softwrap_point(CharOffset::from(10)),
SoftWrapPoint::new(2, char_x(2))
SoftWrapPoint::new(2, px(char_x(2)))
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(2, char_x(2))),
model.softwrap_point_to_offset(SoftWrapPoint::new(2, px(char_x(2)))),
CharOffset::from(10)
);
assert_eq!(
model.offset_to_softwrap_point(CharOffset::from(19)),
SoftWrapPoint::new(4, char_x(2))
SoftWrapPoint::new(4, px(char_x(2)))
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(4, char_x(2))),
model.softwrap_point_to_offset(SoftWrapPoint::new(4, px(char_x(2)))),
CharOffset::from(19)
);
// Softwrapping on an empty line should work.
assert_eq!(
model.offset_to_softwrap_point(CharOffset::from(21)),
SoftWrapPoint::new(5, TEXT_SPACING.left_offset())
SoftWrapPoint::new(5, px(TEXT_SPACING.left_offset()))
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(5, Pixels::zero())),
model.softwrap_point_to_offset(SoftWrapPoint::new(5, ColumnUnit::pixels_zero())),
CharOffset::from(21)
);
// Out of bound points should be bounded to the trailing newline.
assert_eq!(
model.offset_to_softwrap_point(CharOffset::from(40)),
SoftWrapPoint::new(8, Pixels::zero())
SoftWrapPoint::new(8, ColumnUnit::pixels_zero())
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(7, Pixels::zero())),
model.softwrap_point_to_offset(SoftWrapPoint::new(7, ColumnUnit::pixels_zero())),
CharOffset::from(26)
);
// Points are bounded to their line's contents.
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(5, char_x(3))),
model.softwrap_point_to_offset(SoftWrapPoint::new(5, px(char_x(3)))),
CharOffset::from(21)
);
assert_eq!(
model.softwrap_point_to_offset(SoftWrapPoint::new(5, char_x(2))),
model.softwrap_point_to_offset(SoftWrapPoint::new(5, px(char_x(2)))),
CharOffset::from(21)
);
}
@@ -280,39 +279,38 @@ fn test_character_bounds() {
));
model.set_content(content);
// Due to the minimum block height, there is 6px of top spacing. In addition, there's a 4px
// left margin.
// Due to the minimum block height, there is 2px of top spacing.
let char_size = vec2f(10., 10.);
// The middle of the first line.
assert_eq!(
model.character_bounds(2.into()),
Some(RectF::new(vec2f(24., 6.), char_size))
Some(RectF::new(vec2f(20., 2.), char_size))
);
// The first character of the second soft-wrapped line.
assert_eq!(
model.character_bounds(4.into()),
Some(RectF::new(vec2f(4., 16.), char_size))
Some(RectF::new(vec2f(0., 12.), char_size))
);
// The middle of the first line of the second paragraph.
assert_eq!(
model.character_bounds(9.into()),
Some(RectF::new(vec2f(14., 38.), char_size))
Some(RectF::new(vec2f(10., 26.), char_size))
);
// The end of the first line of the second paragraph.
assert_eq!(
model.character_bounds(11.into()),
Some(RectF::new(vec2f(34., 38.), char_size))
Some(RectF::new(vec2f(30., 26.), char_size))
);
// The middle of the second line of the second paragraph.
assert_eq!(
model.character_bounds(13.into()),
Some(RectF::new(vec2f(14., 48.), char_size))
Some(RectF::new(vec2f(10., 36.), char_size))
);
}
@@ -336,6 +334,7 @@ fn test_non_empty_content_can_hide_final_trailing_newline() {
model.viewport.width().as_f32(),
)),
code_block_type: Default::default(),
pending_mermaid_asset: None,
});
model.set_content(content);
@@ -353,7 +352,7 @@ fn test_empty_content_keeps_final_trailing_newline_when_suppressed() {
model.set_show_final_trailing_newline_when_non_empty(false);
assert_eq!(model.blocks(), 1);
assert_eq!(model.height(), 32.0.into_pixels());
assert_eq!(model.height(), 24.0.into_pixels());
}
#[test]
@@ -612,6 +611,7 @@ fn test_first_line_bounds() {
model.viewport.width().as_f32(),
)),
code_block_type: Default::default(),
pending_mermaid_asset: None,
});
model.set_content(content);
@@ -619,12 +619,12 @@ fn test_first_line_bounds() {
let text_block = content
.block_at_offset(CharOffset::zero())
.expect("Block should exist");
// Because the paragraph is soft-wrapped, it doesn't need centering, so the top offset is 4px.
// Because the paragraph is soft-wrapped, it doesn't need centering.
assert_eq!(
text_block.first_line_bounds().expect("Bounds should exist"),
RectF::new(vec2f(0., 4.), vec2f(104., 10.))
RectF::new(vec2f(0., 0.), vec2f(100., 10.))
);
assert_eq!(text_block.item.height().as_f32(), 48.);
assert_eq!(text_block.item.height().as_f32(), 40.);
let list_block = content
.block_at_offset(CharOffset::from(33))
@@ -632,7 +632,7 @@ fn test_first_line_bounds() {
assert_eq!(
list_block.first_line_bounds().expect("Bounds should exist"),
RectF::new(
vec2f(0., 52.),
vec2f(0., 44.),
vec2f(
64., /* 4px margin + 20px list padding + 40px of text */
10.
@@ -649,7 +649,7 @@ fn test_first_line_bounds() {
.first_line_bounds()
.expect("Bounds should exist"),
RectF::new(
vec2f(0., 70. /* 66px y-offset + 4px margin */),
vec2f(0., 62. /* 58px y-offset + 4px margin */),
vec2f(
144., /* 4px margin + 40px list padding + 10px of text - the test layout logic doesn't account for spacing */
10.
@@ -664,7 +664,7 @@ fn test_first_line_bounds() {
assert_eq!(
code_block.first_line_bounds().expect("Bounds should exist"),
RectF::new(
vec2f(0., 112. /* 104px y-offset + 8px margin */),
vec2f(0., 104. /* 96px y-offset + 8px margin */),
vec2f(
70., /* 4px margin + 16px padding + 50px text */
16. /* 16px padding area */
@@ -684,10 +684,8 @@ fn test_first_line_bounds() {
.first_line_bounds()
.expect("Bounds should exist"),
RectF::new(
vec2f(
0., 219., /* 198px y-offset + 14px margin + 7px centering */
),
vec2f(5. /* 4px margin + 1px cursor */, 10.)
vec2f(0., 207. /* 200px y-offset + 7px centering */,),
vec2f(1. /* 1px cursor */, 10.)
)
)
}
@@ -715,8 +713,8 @@ fn test_scroll_snapshot() {
layout_content(&mut model);
let content = model.content();
// Verify the height of each block. Each text paragraph has 8px of vertical padding and 10px
// per soft-wrapped line. The trailing newline block is 32px high.
// Verify the height of each block. Each text paragraph has 10px per soft-wrapped line with a
// 24px minimum height. The trailing newline block is 24px high.
assert_eq!(
content
.block_at_offset(CharOffset::zero())
@@ -724,7 +722,7 @@ fn test_scroll_snapshot() {
.item
.height()
.as_f32(),
38.
30.
);
assert_eq!(
content
@@ -733,7 +731,7 @@ fn test_scroll_snapshot() {
.item
.height()
.as_f32(),
48.
40.
);
assert_eq!(
content
@@ -742,14 +740,14 @@ fn test_scroll_snapshot() {
.item
.height()
.as_f32(),
32.
24.
);
drop(content);
// Scroll so that the EEEE line is at the top of the viewport.
model.viewport.scroll((-52.).into_pixels(), model.height());
model.viewport.scroll((-44.).into_pixels(), model.height());
let scroll_position = model.snapshot_scroll_position();
assert_eq!(scroll_position.first_character_offset(), 17.into());
assert_eq!(scroll_position.first_character_offset(), 13.into());
// Now, double the viewport width, halving the number of soft-wrapped lines.
model
@@ -758,11 +756,11 @@ fn test_scroll_snapshot() {
// At first, the content will not have been laid out again, so the scroll position is
// unaffected.
assert_eq!(model.viewport.scroll_top(), 52.0.into_pixels());
// After laying out again, each block is exactly 32px high (the two soft-wrapped blocks are
assert_eq!(model.viewport.scroll_top(), 34.0.into_pixels());
// After laying out again, each block is exactly 24px high (the two soft-wrapped blocks are
// below the minimum height otherwise).
layout_content(&mut model);
assert_eq!(model.height().as_f32(), 32. * 3.);
assert_eq!(model.height().as_f32(), 24. * 3.);
// Restore the scroll position at the new height. It should still start at the same content.
assert!(
@@ -770,16 +768,15 @@ fn test_scroll_snapshot() {
.viewport
.scroll_to(scroll_position.to_scroll_top(&model), model.height())
);
// The new scroll position is 32px (the first block) plus 4px of padding on the second block.
// The EEEE line is now part of that first line.
assert_eq!(model.viewport.scroll_top().as_f32(), 36.);
// The reduced content height clamps the restored position to the last viewport.
assert_eq!(model.viewport.scroll_top().as_f32(), 12.);
// Halve the original viewport width, leading to twice as many soft-wrapped lines.
model
.viewport
.set_size(vec2f(20., 60.), model.width(), model.height());
layout_content(&mut model);
assert_eq!(model.height().as_f32(), 68. + 88. + 32.);
assert_eq!(model.height().as_f32(), 60. + 80. + 24.);
// Restore the scroll position at the new height.
assert!(
@@ -787,8 +784,8 @@ fn test_scroll_snapshot() {
.viewport
.scroll_to(scroll_position.to_scroll_top(&model), model.height())
);
// The new scroll position is on the third soft-wrapped line of the second paragraph.
assert_eq!(model.viewport.scroll_top().as_f32(), 92.);
// The new scroll position is at the start of the second paragraph.
assert_eq!(model.viewport.scroll_top().as_f32(), 60.);
}
#[test]
@@ -1158,17 +1155,17 @@ fn make_test_cell_layout() -> CellLayout {
line_char_ranges: vec![CharOffset::from(0)..CharOffset::from(3)],
line_widths: vec![30.0],
line_caret_positions: vec![vec![
galaxyui::text_layout::CaretPosition {
galaxyui_core::text_layout::CaretPosition {
position_in_line: 0.0,
start_offset: 0,
last_offset: 0,
},
galaxyui::text_layout::CaretPosition {
galaxyui_core::text_layout::CaretPosition {
position_in_line: 10.0,
start_offset: 1,
last_offset: 1,
},
galaxyui::text_layout::CaretPosition {
galaxyui_core::text_layout::CaretPosition {
position_in_line: 20.0,
start_offset: 2,
last_offset: 2,
@@ -1429,3 +1426,245 @@ fn test_link_at_offset_uses_cached_cell_links() {
assert_eq!(table.link_at_offset(CharOffset::from(0)), None);
assert_eq!(table.link_at_offset(CharOffset::from(3)), None);
}
// ─────────────────────────────────────────────────────────────────────────────
// CharCell (TUI) layout helper tests
// ─────────────────────────────────────────────────────────────────────────────
mod char_cell {
use crate::render::model::{
ColumnUnit, LineCount, SoftWrapPoint, char_cell_display_width, char_cell_line_row_starts,
char_cell_max_line, char_cell_offset_to_softwrap_point, char_cell_softwrap_point_to_offset,
};
/// Build the `(line_starts, char_widths)` pair from a text string (mirrors
/// `CharCellState::update_text` logic) so tests can construct the
/// char-cell layout inputs without a full `RenderState`. `char_widths` holds
/// the per-char display width (the derived data the layout actually needs).
fn line_starts_for(text: &str) -> (Vec<usize>, Vec<u8>) {
let char_widths: Vec<u8> = text
.chars()
.map(|c| char_cell_display_width(c) as u8)
.collect();
let mut starts = vec![0_usize];
for (i, ch) in text.chars().enumerate() {
if ch == '\n' {
starts.push(i + 1);
}
}
(starts, char_widths)
}
#[test]
fn max_line_empty() {
let (starts, widths) = line_starts_for("");
// Empty content → 1 visual row.
assert_eq!(char_cell_max_line(&starts, &widths, 80), LineCount(1));
}
#[test]
fn max_line_single_short_line() {
let (starts, widths) = line_starts_for("hello");
assert_eq!(char_cell_max_line(&starts, &widths, 80), LineCount(1));
}
#[test]
fn max_line_single_wrapping_line() {
// 10 chars, width 4 → ceil(10/4) = 3 rows.
let (starts, widths) = line_starts_for("0123456789");
assert_eq!(char_cell_max_line(&starts, &widths, 4), LineCount(3));
}
#[test]
fn max_line_two_logical_lines() {
// "abc\ndef": line0 = 3 chars (1 row at width 10), line1 = 3 chars (1 row) → 2.
let (starts, widths) = line_starts_for("abc\ndef");
assert_eq!(char_cell_max_line(&starts, &widths, 10), LineCount(2));
}
#[test]
fn max_line_empty_logical_line() {
// "\n": two logical lines, both empty → 2 rows.
let (starts, widths) = line_starts_for("\n");
assert_eq!(char_cell_max_line(&starts, &widths, 80), LineCount(2));
}
#[test]
fn offset_to_softwrap_single_line_short() {
let text = "hello";
let (starts, widths) = line_starts_for(text);
// The softwrap API is 0-based, so char 'h' = index 0, 'e' = 1, ...
// 'h' should be at (row=0, col=0).
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(0), &starts, &widths, 80);
assert_eq!(pt, SoftWrapPoint::new(0, ColumnUnit::Chars(0)));
// 'l' (3rd char, index 2) at col 2.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(2), &starts, &widths, 80);
assert_eq!(pt, SoftWrapPoint::new(0, ColumnUnit::Chars(2)));
}
#[test]
fn offset_to_softwrap_wrapping_line() {
// width=4, "0123456789" — char index 4 should be on row 1, col 0.
let text = "0123456789";
let (starts, widths) = line_starts_for(text);
// index 4 → row 4/4=1, col 0.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(4), &starts, &widths, 4);
assert_eq!(pt, SoftWrapPoint::new(1, ColumnUnit::Chars(0)));
// index 7 → row 7/4=1, col 7%4=3.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(7), &starts, &widths, 4);
assert_eq!(pt, SoftWrapPoint::new(1, ColumnUnit::Chars(3)));
// index 9 → row 9/4=2, col 9%4=1.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(9), &starts, &widths, 4);
assert_eq!(pt, SoftWrapPoint::new(2, ColumnUnit::Chars(1)));
}
#[test]
fn offset_to_softwrap_two_logical_lines() {
// "abc\ndef", width=10
// 'a'=index0→(row0,col0), 'd'=index4→(row1,col0)
let text = "abc\ndef";
let (starts, widths) = line_starts_for(text);
let pt_a = char_cell_offset_to_softwrap_point(CharOffset::from(0), &starts, &widths, 10);
assert_eq!(pt_a, SoftWrapPoint::new(0, ColumnUnit::Chars(0)));
// 'd' = index 4 (after 'abc\n'). Logical line 1, offset_in_line=0.
let pt_d = char_cell_offset_to_softwrap_point(CharOffset::from(4), &starts, &widths, 10);
assert_eq!(pt_d, SoftWrapPoint::new(1, ColumnUnit::Chars(0)));
}
#[test]
fn softwrap_roundtrip_single_line() {
let text = "hello world";
let (starts, widths) = line_starts_for(text);
for i in 0..=(widths.len() as u64) {
let offset = CharOffset::from(i as usize);
let pt = char_cell_offset_to_softwrap_point(offset, &starts, &widths, 80);
// Verify the column is ColumnUnit::Chars
assert!(
matches!(pt.column(), ColumnUnit::Chars(_)),
"index {i}: expected Chars variant"
);
let back = char_cell_softwrap_point_to_offset(pt, &starts, &widths, 80);
assert_eq!(back, offset, "round-trip failed at index {i}");
}
}
#[test]
fn softwrap_roundtrip_wrapping() {
let text = "abcdefghij"; // 10 chars
let (starts, widths) = line_starts_for(text);
for i in 0..10 {
let offset = CharOffset::from(i);
let pt = char_cell_offset_to_softwrap_point(offset, &starts, &widths, 4);
let back = char_cell_softwrap_point_to_offset(pt, &starts, &widths, 4);
assert_eq!(back, offset, "round-trip failed at index {i} with width=4");
}
}
#[test]
fn softwrap_point_to_offset_clamps_to_shorter_final_line() {
// "abcd\nx": logical line 0 = "abcd" (4 chars), final line = "x" (1 char).
// Moving down from column 3 of the first line targets (row 1, col 3),
// but the final line only has 1 char — the result must clamp to the end
// of the buffer (offset 6 = total chars), never past it.
let text = "abcd\nx";
let (starts, widths) = line_starts_for(text);
assert_eq!(widths.len(), 6);
let pt = SoftWrapPoint::new(1, ColumnUnit::Chars(3));
let offset = char_cell_softwrap_point_to_offset(pt, &starts, &widths, 80);
// Final line starts at char index 5 ("x"); clamped end is 5 + 1 = 6.
assert_eq!(offset, CharOffset::from(6));
assert!(
offset <= CharOffset::from(widths.len()),
"offset {offset:?} must not exceed total chars {}",
widths.len()
);
}
#[test]
fn softwrap_returns_chars_variant_not_pixels() {
let text = "abc";
let (starts, widths) = line_starts_for(text);
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(0), &starts, &widths, 80);
assert!(
matches!(pt.column(), ColumnUnit::Chars(_)),
"CharCell path must return ColumnUnit::Chars, got {:?}",
pt.column()
);
}
#[test]
fn softwrap_point_zero_offset_is_row0_col0() {
let text = "abc";
let (starts, widths) = line_starts_for(text);
// Index 0 = first char → (0, 0).
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(0), &starts, &widths, 80);
assert_eq!(pt.row(), 0);
assert_eq!(pt.column(), ColumnUnit::Chars(0));
}
// ── Unicode display width ───────────────────────────────────────────────
#[test]
fn display_width_basic() {
assert_eq!(char_cell_display_width('a'), 1);
// CJK ideographs are double-width.
assert_eq!(char_cell_display_width('你'), 2);
// A combining acute accent is zero-width.
assert_eq!(char_cell_display_width('\u{0301}'), 0);
}
#[test]
fn wide_char_occupies_two_columns() {
// "你好world": 你(2) 好(2) w o r l d. Index 2 ('w') sits at display col 4.
let text = "你好world";
let (starts, widths) = line_starts_for(text);
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(2), &starts, &widths, 80);
assert_eq!(pt, SoftWrapPoint::new(0, ColumnUnit::Chars(4)));
}
#[test]
fn wide_char_wraps_when_it_does_not_fit() {
// "你好你" at width 4: 你好 fill the first row (4 cols); the third 你
// doesn't fit so it wraps to row 1.
let text = "你好你";
let (starts, widths) = line_starts_for(text);
assert_eq!(char_cell_max_line(&starts, &widths, 4), LineCount(2));
// Cursor before the third 你 (index 2) is at the start of row 1.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(2), &starts, &widths, 4);
assert_eq!(pt, SoftWrapPoint::new(1, ColumnUnit::Chars(0)));
// Round-trips at each char boundary.
for i in 0..=widths.len() {
let offset = CharOffset::from(i);
let pt = char_cell_offset_to_softwrap_point(offset, &starts, &widths, 4);
let back = char_cell_softwrap_point_to_offset(pt, &starts, &widths, 4);
assert_eq!(back, offset, "wide-char round-trip failed at index {i}");
}
}
#[test]
fn zero_width_char_does_not_advance_column() {
// "a\u{0301}b": 'a' + combining acute (0 width) + 'b'. The combining
// mark shares 'a's column, so 'b' sits at col 1 (not 2).
let text = "a\u{0301}b";
let (starts, widths) = line_starts_for(text);
// Gap before 'b' (index 2) shares the accent's column.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(2), &starts, &widths, 80);
assert_eq!(pt, SoftWrapPoint::new(0, ColumnUnit::Chars(1)));
// End of line (index 3, after 'b') is at col 2.
let pt = char_cell_offset_to_softwrap_point(CharOffset::from(3), &starts, &widths, 80);
assert_eq!(pt, SoftWrapPoint::new(0, ColumnUnit::Chars(2)));
}
#[test]
fn line_row_starts_breaks_on_wide_chars() {
// width 4, "你好你好": two wide chars per row → break before index 2.
let widths: Vec<u8> = "你好你好"
.chars()
.map(|c| char_cell_display_width(c) as u8)
.collect();
assert_eq!(char_cell_line_row_starts(&widths, 4), vec![0, 2]);
// width 0 disables wrapping.
assert_eq!(char_cell_line_row_starts(&widths, 0), vec![0]);
}
}
+1 -2
View File
@@ -2,7 +2,6 @@ use std::ops::{Add, Sub};
use itertools::Itertools;
use num_traits::SaturatingSub;
use string_offset::CharOffset;
use super::FrameOffset;
@@ -19,7 +18,7 @@ mod tests;
/// certain character runs are interactive. Within those runs, there's a 1:1 mapping between
/// `char`s in the content model and `char`s in the text frame. It translates in two directions:
/// * From a [`CharOffset`] relative to the start of the content model block (a [`super::Paragraph`])
/// to the character index in the [`galaxyui::text_layout::TextFrame`].
/// to the character index in the [`galaxyui_core::text_layout::TextFrame`].
/// * From a [`FrameOffset`] in the `TextFrame` to the closest `CharOffset` in the content model
/// block (for example, clicking within a placeholder should snap the cursor to a regular content
/// character).
@@ -1,6 +1,7 @@
use string_offset::CharOffset;
use super::{OffsetMap, SelectableTextRun};
use crate::render::model::FrameOffset;
use string_offset::CharOffset;
#[test]
fn test_offset_map_basic() {
@@ -76,24 +77,20 @@ fn test_offset_map_placeholders() {
fn test_end_to_end() {
// Group imports here so they don't cause "unused import" warnings on other targets.
use galaxyui::{
App, color::ColorU, elements::Fill, fonts::Cache as FontCache, text_layout::LayoutCache,
};
use galaxyui_core::App;
use galaxyui_core::color::ColorU;
use galaxyui_core::elements::Fill;
use galaxyui_core::fonts::Cache as FontCache;
use galaxyui_core::text_layout::LayoutCache;
use crate::{
content::{
buffer::{Buffer, BufferEditAction, EditOrigin},
selection_model::BufferSelectionModel,
text::IndentBehavior,
},
render::{
layout::TextLayout,
model::{
BlockItem, BrokenLinkStyle, CheckBoxStyle, HorizontalRuleStyle, InlineCodeStyle,
PARAGRAPH_MIN_HEIGHT, ParagraphStyles, RenderLayoutOptions, RichTextStyles,
TableStyle, test_utils::TEST_BASELINE_OFFSET,
},
},
use crate::content::buffer::{Buffer, BufferEditAction, EditOrigin};
use crate::content::selection_model::BufferSelectionModel;
use crate::content::text::IndentBehavior;
use crate::render::layout::TextLayout;
use crate::render::model::test_utils::TEST_BASELINE_OFFSET;
use crate::render::model::{
BlockItem, BrokenLinkStyle, CheckBoxStyle, HorizontalRuleStyle, InlineCodeStyle,
PARAGRAPH_MIN_HEIGHT, ParagraphStyles, RenderLayoutOptions, RichTextStyles, TableStyle,
};
App::test((), |mut app| async move {
@@ -214,7 +211,7 @@ fn test_end_to_end() {
delta.layout_delta(
&text_layout,
None,
RenderLayoutOptions::default(),
&RenderLayoutOptions::default(),
None,
ctx,
)
+5 -8
View File
@@ -3,20 +3,17 @@
use std::sync::Arc;
use galaxyui::{
geometry::vector::Vector2F,
text_layout::Line,
units::{IntoPixels, Pixels},
};
use sum_tree::{Cursor, Dimension};
use crate::render::layout::line_height;
use string_offset::CharOffset;
use sum_tree::{Cursor, Dimension};
use galaxyui_core::geometry::vector::Vector2F;
use galaxyui_core::text_layout::Line;
use galaxyui_core::units::{IntoPixels, Pixels};
use super::{
BlockItem, BlockSpacing, HorizontalRuleConfig, ImageBlockConfig, LaidOutEmbeddedItem,
LaidOutTable, LayoutSummary, LineCount, Paragraph, ParagraphBlock, RenderContext, bounds,
};
use crate::render::layout::line_height;
/// Wrapper to track an item's position, both in the buffer and on the screen.
#[derive(Debug)]
@@ -1,4 +1,4 @@
use galaxyui::EntityId;
use galaxyui_core::EntityId;
/// Utility for consistently creating and referencing saved position IDs for
/// rich text.
@@ -1,7 +1,7 @@
use super::*;
use markdown_parser::Hyperlink;
use markdown_parser::parse_inline_markdown;
use markdown_parser::weight::CustomWeight;
use markdown_parser::{Hyperlink, parse_inline_markdown};
use super::*;
#[test]
fn test_simple_table() {
+13 -15
View File
@@ -1,26 +1,24 @@
//! Test helpers for all render model tests.
use parking_lot::Once;
use std::{mem, sync::Arc};
use vec1::{Vec1, vec1};
use std::mem;
use std::sync::Arc;
use crate::content::text::BufferBlockStyle;
use galaxyui::elements::ListIndentLevel;
use galaxyui::{
color::ColorU,
elements::{Border, Fill},
fonts::{FamilyId, Weight},
geometry::vector::vec2f,
text_layout::{CaretPosition, Glyph, Line, Run, TextFrame},
units::{IntoPixels, Pixels},
};
use ordered_float::OrderedFloat;
use parking_lot::Once;
use vec1::{Vec1, vec1};
use galaxyui_core::color::ColorU;
use galaxyui_core::elements::{Border, Fill, ListIndentLevel};
use galaxyui_core::fonts::{FamilyId, Weight};
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::text_layout::{CaretPosition, Glyph, Line, Run, TextFrame};
use galaxyui_core::units::{IntoPixels, Pixels};
use super::{
BlockItem, BrokenLinkStyle, CheckBoxStyle, DEFAULT_BLOCK_SPACINGS, HorizontalRuleStyle,
InlineCodeStyle, OffsetMap, PARAGRAPH_MIN_HEIGHT, Paragraph, ParagraphStyles, RichTextStyles,
TEXT_SPACING, TableStyle,
};
use crate::content::text::BufferBlockStyle;
pub const TEST_BASELINE_OFFSET: f32 = 0.7;
@@ -241,7 +239,7 @@ pub fn layout(text: &str, styles: &RichTextStyles, max_width: impl IntoPixels) -
width: line_width.as_f32(),
trailing_whitespace_width: 0.,
runs: vec![Run {
font_id: galaxyui::fonts::FontId(0),
font_id: galaxyui_core::fonts::FontId(0),
styles: Default::default(),
glyphs: mem::take(&mut glyphs_acc),
width: line_width.as_f32(),
@@ -280,7 +278,7 @@ pub fn layout(text: &str, styles: &RichTextStyles, max_width: impl IntoPixels) -
width: line_width.as_f32(),
trailing_whitespace_width: 0.,
runs: vec![Run {
font_id: galaxyui::fonts::FontId(0),
font_id: galaxyui_core::fonts::FontId(0),
styles: Default::default(),
glyphs: glyphs_acc,
width: line_width.as_f32(),
+8 -12
View File
@@ -1,21 +1,17 @@
use float_cmp::ApproxEq;
use galaxyui::{
SizeConstraint,
geometry::{
rect::RectF,
vector::{Vector2F, vec2f},
},
units::{IntoPixels, Pixels},
};
use sum_tree::{SeekBias, SumTree};
use crate::render::element::RenderContext;
use string_offset::CharOffset;
use sum_tree::{SeekBias, SumTree};
use galaxyui_core::SizeConstraint;
use galaxyui_core::geometry::rect::RectF;
use galaxyui_core::geometry::vector::{Vector2F, vec2f};
use galaxyui_core::units::{IntoPixels, Pixels};
use super::positioned::PositionedCursor;
use super::{
AUTO_SCROLL_MARGIN, BlockItem, BlockSpacing, Height, HitTestOptions, LayoutSummary, Location,
RenderState, UNIT_MARGIN, bounds, positioned::PositionedCursor,
RenderState, UNIT_MARGIN, bounds,
};
use crate::render::element::RenderContext;
/// For horizontal autoscrolling, it is very easy to "stuck" on a character if it is aligned exactly on the viewport boundary.
/// To help make scrolling more smooth, add a small margin here to overcome these boundaries.
@@ -1,18 +1,12 @@
use itertools::Itertools;
use sum_tree::SumTree;
use galaxyui::{
SizeConstraint,
geometry::vector::vec2f,
units::{IntoPixels, Pixels},
};
use crate::render::model::{
RenderState,
test_utils::{TEST_STYLES, mock_paragraph},
};
use galaxyui_core::SizeConstraint;
use galaxyui_core::geometry::vector::vec2f;
use galaxyui_core::units::{IntoPixels, Pixels};
use super::ViewportState;
use crate::render::model::RenderState;
use crate::render::model::test_utils::{TEST_STYLES, mock_paragraph};
#[test]
fn test_viewport_offsets() {
@@ -26,7 +20,7 @@ fn test_viewport_offsets() {
content.push(mock_paragraph(80., 100., 1));
render_state.set_content(content);
// Double-check the heights with margins+padding, as later tests rely on them.
// Double-check the heights, as later tests rely on them.
let heights = render_state
.content
.borrow()
@@ -34,7 +28,7 @@ fn test_viewport_offsets() {
.iter()
.map(|item| item.height().as_f32())
.collect_vec();
assert_eq!(heights, vec![32., 68., 108., 38., 88., 32.]);
assert_eq!(heights, vec![24., 60., 100., 30., 80., 24.]);
let content = render_state.content();
let offsets = content
@@ -53,12 +47,14 @@ fn test_viewport_offsets() {
vec![
// The first item is fully above the viewport.
// The second item is slightly above the viewport.
(-8., 1),
(-16., 1),
// The third is fully within the viewport
(60., 2),
(44., 2),
// The fourth is slightly past the viewport, and cut off.
(168., 4)
] // The fifth item is fully after the viewport.
(144., 4),
// The fifth is slightly past the viewport, and cut off.
(174., 7)
]
);
}