diff --git a/egui/src/experimental/easy_mark_parser.rs b/egui/src/experimental/easy_mark_parser.rs index 7d7840946..13487e761 100644 --- a/egui/src/experimental/easy_mark_parser.rs +++ b/egui/src/experimental/easy_mark_parser.rs @@ -13,6 +13,7 @@ #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Item<'a> { /// `\n` + // TODO: add Style here so empty heading still uses up the right amount of space. Newline, /// Text(Style, &'a str), @@ -48,6 +49,10 @@ pub struct Style { pub strikethrough: bool, /// /italics/ pub italics: bool, + /// $small$ + pub small: bool, + /// ^raised^ + pub raised: bool, } /// Parser for the `EasyMark` markup language. @@ -292,6 +297,18 @@ impl<'a> Iterator for Parser<'a> { self.style.italics = !self.style.italics; continue; } + if let Some(rest) = self.s.strip_prefix('$') { + self.s = rest; + self.start_of_line = false; + self.style.small = !self.style.small; + continue; + } + if let Some(rest) = self.s.strip_prefix('^') { + self.s = rest; + self.start_of_line = false; + self.style.raised = !self.style.raised; + continue; + } // `` or `[link](url)` if let Some(item) = self.url() { @@ -301,7 +318,7 @@ impl<'a> Iterator for Parser<'a> { // Swallow everything up to the next special character: let end = self .s - .find(&['*', '`', '~', '_', '/', '\\', '<', '[', '\n'][..]) + .find(&['*', '`', '~', '_', '/', '$', '^', '\\', '<', '[', '\n'][..]) .map(|special| special.max(1)) // make sure we swallow at least one character .unwrap_or_else(|| self.s.len()); diff --git a/egui/src/experimental/easy_mark_viewer.rs b/egui/src/experimental/easy_mark_viewer.rs index dbc6c3561..b82948df7 100644 --- a/egui/src/experimental/easy_mark_viewer.rs +++ b/egui/src/experimental/easy_mark_viewer.rs @@ -84,12 +84,19 @@ fn label_from_style(text: &str, style: &easy_mark::Style) -> Label { underline, strikethrough, italics, + small, + raised, } = *style; + let small = small || raised; // Raised text is also smaller + let mut label = Label::new(text); - if heading { + if heading && !small { label = label.heading().strong(); } + if small && !heading { + label = label.small(); + } if code { label = label.code(); } @@ -107,6 +114,9 @@ fn label_from_style(text: &str, style: &easy_mark::Style) -> Label { if italics { label = label.italics(); } + if raised { + label = label.raised(); + } label } diff --git a/egui/src/widgets/label.rs b/egui/src/widgets/label.rs index 27e833613..97e1b28bb 100644 --- a/egui/src/widgets/label.rs +++ b/egui/src/widgets/label.rs @@ -23,6 +23,7 @@ pub struct Label { strikethrough: bool, underline: bool, italics: bool, + raised: bool, } impl Label { @@ -39,6 +40,7 @@ impl Label { strikethrough: false, underline: false, italics: false, + raised: false, } } @@ -112,10 +114,22 @@ impl Label { self } + /// Smaller text pub fn small(self) -> Self { self.text_style(TextStyle::Small) } + /// For e.g. exponents + pub fn small_raised(self) -> Self { + self.text_style(TextStyle::Small).raised() + } + + /// Align text to top + pub fn raised(mut self) -> Self { + self.raised = true; + self + } + /// Fill-color behind the text pub fn background_color(mut self, background_color: impl Into) -> Self { self.background_color = background_color.into(); @@ -171,6 +185,7 @@ impl Label { strikethrough, underline, italics, + raised: _, // TODO .. } = *self; @@ -257,6 +272,7 @@ impl Widget for Label { let text_style = self.text_style_or_default(ui.style()); let font = &ui.fonts()[text_style]; + let font_height = font.row_height(); let mut galley = font.layout_multiline_with_indentation_and_max_width( self.text.clone(), first_row_indentation, @@ -270,18 +286,35 @@ impl Widget for Label { let id = ui.advance_cursor_after_rect(rect); let mut response = ui.interact(rect, id, sense); - let mut y_translation = 0.0; - if let Some(row) = galley.rows.get(1) { - // We could be sharing the first row with e.g. a button, that is higher than text. - // So we need to compensate for that: - if pos.y + row.y_min < ui.min_rect().bottom() { - y_translation = ui.min_rect().bottom() - row.y_min - pos.y; + // We could be sharing the first row with e.g. a button which is higher than text. + // So we need to compensate for that: + if let Some(row) = galley.rows.get_mut(1) { + let cursor = ui.cursor(); + + if pos.y + row.y_min < cursor.bottom() { + let y_translation = cursor.bottom() - row.y_min - pos.y; + + if y_translation != 0.0 { + for row in galley.rows.iter_mut().skip(1) { + row.y_min += y_translation; + row.y_max += y_translation; + } + } + } + } + + if text_style == TextStyle::Small && !self.raised { + for row in galley.rows.iter_mut() { + // center: + let normal_text_heigth = ui.fonts()[TextStyle::Body].row_height(); // TODO: get from ui + let dy = (normal_text_heigth - font_height) / 2.0; + + row.y_min += dy; + row.y_max += dy; } } for row in galley.rows.iter_mut().skip(1) { - row.y_min += y_translation; - row.y_max += y_translation; let rect = row.rect().translate(vec2(pos.x, pos.y)); response |= ui.allocate_rect(rect, sense); } diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index 97142f286..76571c65f 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -13,7 +13,7 @@ pub(crate) struct State { #[derive(Clone, Copy, Debug, Default)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -struct CursorPair { +pub struct CursorPair { /// When selecting with a mouse, this is where the mouse was released. /// When moving with e.g. shift+arrows, this is what moves. /// Note that the two ends can come in any order, and also be equal (no selection). @@ -133,6 +133,14 @@ pub struct TextEdit<'t> { desired_width: Option, desired_height_rows: usize, } +impl<'t> TextEdit<'t> { + pub fn cursor(ui: &Ui, id: Id) -> Option { + ui.memory() + .text_edit + .get(&id) + .and_then(|state| state.cursorp) + } +} impl<'t> TextEdit<'t> { #[deprecated = "Use `TextEdit::singleline` or `TextEdit::multiline` (or the helper `ui.text_edit_singleline`, `ui.text_edit_multiline`) instead"] diff --git a/egui_demo_lib/src/apps/easy_mark_editor.rs b/egui_demo_lib/src/apps/easy_mark_editor.rs index 735d21d1b..8c0d2b3cf 100644 --- a/egui_demo_lib/src/apps/easy_mark_editor.rs +++ b/egui_demo_lib/src/apps/easy_mark_editor.rs @@ -37,8 +37,9 @@ impl EasyMarkEditor { ScrollArea::auto_sized() .id_source("source") .show(&mut columns[0], |ui| { - // ui.text_edit_multiline(&mut self.code); ui.add(TextEdit::multiline(&mut self.code).text_style(TextStyle::Monospace)); + // let cursor = TextEdit::cursor(response.id); + // TODO: cmd-i, cmd-b, etc for italics, bold, .... }); ScrollArea::auto_sized() .id_source("rendered") @@ -62,7 +63,7 @@ and is also missing some features. # At a glance - inline text: - - normal, `code`, *strong*, ~strikethrough~, _underline_, /italics/ + - normal, `code`, *strong*, ~strikethrough~, _underline_, /italics/, ^raised^, $small$ - `\` escapes the next character - [hyperlink](https://github.com/emilk/egui) - Embedded URL: @@ -72,6 +73,8 @@ and is also missing some features. - `- ` bullet list - `1. ` numbered list - \`\`\` code fence +- You can write exponents like so: x^2^ +- Remember to read the small print $(terms and conditions apply)$ # Design > /"Why do what everyone else is doing, when everyone else is already doing it?" @@ -102,9 +105,11 @@ Escaping the newline effectively ignores it. The style characters are chosen to be similar to what they are representing: `_` = _underline_ - `~` = ~strikethrough~ (`-` is too common in normal text) + `~` = ~strikethrough~ (`-` is used for bullet points) `/` = /italics/ `*` = *strong* + `$` = $small$ + `^` = ^raised^ # TODO - Sub-headers (`## h2`, `### h3` etc) @@ -113,5 +118,9 @@ The style characters are chosen to be similar to what they are representing: - centering of images is very desirable - captioning (image with a text underneath it) - `![caption=My image][width=200][center](url)` ? +- Nicer URL:s + - `` and `[url](url)` do the same thing yet look completely different. + - let's keep similarity with images - Tables +- "#;