Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
use warpui::{
|
||||
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 super::RenderableBlock;
|
||||
|
||||
pub struct RenderableBrokenEmbedding {
|
||||
row: Box<dyn Element>,
|
||||
viewport_item: ViewportItem,
|
||||
}
|
||||
|
||||
impl RenderableBrokenEmbedding {
|
||||
pub fn new(
|
||||
viewport_item: ViewportItem,
|
||||
styles: &RichTextStyles,
|
||||
model: Option<&dyn EmbeddedItemModel>,
|
||||
ctx: &AppContext,
|
||||
) -> Self {
|
||||
let icon = ConstrainedBox::new(
|
||||
Icon::new(
|
||||
styles.broken_link_style.icon_path,
|
||||
styles.broken_link_style.icon_color,
|
||||
)
|
||||
.with_opacity(1.0)
|
||||
.finish(),
|
||||
)
|
||||
.with_height(styles.base_text.font_size + 2.)
|
||||
.with_width(styles.base_text.font_size + 2.)
|
||||
.finish();
|
||||
|
||||
let text = Container::new(
|
||||
Text::new_inline(
|
||||
"Embed not found",
|
||||
styles.base_text.font_family,
|
||||
styles.base_text.font_size,
|
||||
)
|
||||
.with_color(styles.placeholder_color)
|
||||
.finish(),
|
||||
)
|
||||
.with_padding_left(8.)
|
||||
.finish();
|
||||
|
||||
let mut row = Flex::row()
|
||||
.with_child(icon)
|
||||
.with_child(text)
|
||||
.with_cross_axis_alignment(CrossAxisAlignment::Center);
|
||||
|
||||
if let Some(element) = model.and_then(|model| model.render_remove_embedding_button(ctx)) {
|
||||
row.add_child(Shrinkable::new(1., Empty::new().finish()).finish());
|
||||
row.add_child(Align::new(element).right().finish());
|
||||
}
|
||||
|
||||
Self {
|
||||
viewport_item,
|
||||
row: row.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderableBlock for RenderableBrokenEmbedding {
|
||||
fn viewport_item(&self) -> &ViewportItem {
|
||||
&self.viewport_item
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
model: &crate::render::model::RenderState,
|
||||
ctx: &mut warpui::LayoutContext,
|
||||
app: &warpui::AppContext,
|
||||
) {
|
||||
self.row.layout(
|
||||
SizeConstraint::strict(vec2f(
|
||||
self.viewport_item.content_size.x(),
|
||||
// Depending on font size, the line height could be bigger
|
||||
// or smaller than font size + 2. (icon size). Choose the larger
|
||||
// of the two to avoid failing to layout the element.
|
||||
model
|
||||
.styles()
|
||||
.base_text
|
||||
.line_height()
|
||||
.as_f32()
|
||||
.max(model.styles().base_text.font_size + 2.),
|
||||
)),
|
||||
ctx,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
model: &crate::render::model::RenderState,
|
||||
ctx: &mut super::RenderContext,
|
||||
app: &warpui::AppContext,
|
||||
) {
|
||||
let content = model.content();
|
||||
let broken_link = extract_block!(self.viewport_item, content, (block, BlockItem::Embedded(item)) => block.embedded(item));
|
||||
|
||||
// Render as selected if the broken link is within any selection.
|
||||
let selected = model.offset_in_active_selection(broken_link.start_char_offset);
|
||||
|
||||
// Draw the cursor if the broken link is at any cursor.
|
||||
let draw_cursor = model.is_selection_head(broken_link.start_char_offset);
|
||||
|
||||
let styles = model.styles();
|
||||
|
||||
let background_rect = self.viewport_item.visible_bounds(ctx);
|
||||
|
||||
ctx.paint
|
||||
.scene
|
||||
.draw_rect_without_hit_recording(background_rect)
|
||||
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
|
||||
.with_background(model.styles().embedding_background);
|
||||
|
||||
let original_content_origin = broken_link.content_origin();
|
||||
let vertical_center_delta =
|
||||
styles.base_text.line_height().as_f32() * (styles.base_text.baseline_ratio - 0.5);
|
||||
|
||||
let content_origin = original_content_origin - vec2f(0., vertical_center_delta);
|
||||
|
||||
if selected {
|
||||
ctx.paint
|
||||
.scene
|
||||
.draw_rect_with_hit_recording(background_rect)
|
||||
.with_background(styles.selection_fill);
|
||||
}
|
||||
|
||||
if draw_cursor {
|
||||
let line_height = styles.base_text.line_height().as_f32();
|
||||
// The lower right corner of the background rect is at reserved_origin + background_rect.size()
|
||||
// Add some horizontal padding and minus line height vertically so it's visible and aligned to
|
||||
// the bottom of the background rect.
|
||||
let end_of_line_position =
|
||||
broken_link.reserved_origin() + background_rect.size() + vec2f(5., -line_height);
|
||||
ctx.draw_and_save_cursor(
|
||||
CursorDisplayType::Bar,
|
||||
end_of_line_position,
|
||||
vec2f(styles.cursor_width, line_height),
|
||||
CursorData::default(),
|
||||
styles,
|
||||
);
|
||||
}
|
||||
|
||||
ctx.paint.scene.start_layer(warpui::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 warpui::AfterLayoutContext, app: &warpui::AppContext) {
|
||||
self.row.after_layout(ctx, app);
|
||||
}
|
||||
|
||||
fn dispatch_event(
|
||||
&mut self,
|
||||
_model: &crate::render::model::RenderState,
|
||||
event: &warpui::event::DispatchedEvent,
|
||||
ctx: &mut warpui::EventContext,
|
||||
app: &AppContext,
|
||||
) -> bool {
|
||||
self.row.dispatch_event(event, ctx, app)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user