r/o

634b7ff902bd44419f7777657d83ff742b664bad parent 1e299c3f

authored by Asherah Connor <ashe@kivikakk.ee> 2 months ago
committed by Asherah Connor <ashe@kivikakk.ee> 2 months ago

Cache glyph size

src/editor.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------
src/main.rs | 68 +++++++++++++++++++++++++++++++------------------
2 files changed, 116 insertions(+), 35 deletions(-)
diff --git a/src/editor.rs b/src/editor.rs
index 131e6c0..25218c9 100644
--- a/src/editor.rs
+++ b/src/editor.rs
@@ -2,12 +2,13 @@ use iced::{
Color, Element, Length, Pixels, Point, Rectangle, Size, Task,
advanced::{
layout::{self, Layout},
- renderer, text,
+ renderer::{self, Quad},
+ text,
widget::{self, Widget},
},
alignment::{Horizontal, Vertical},
border,
- keyboard::{Key, Modifiers},
+ keyboard::{Key, Modifiers, key::Named},
mouse,
widget::text::{LineHeight, Shaping, Wrapping},
};
@@ -37,7 +38,26 @@ impl Editor {
}
}
- pub fn keypress(&mut self, _key: Key, _modifiers: Modifiers) -> Task<Message> {
+ pub fn keypress(&mut self, key: Key, _modifiers: Modifiers) -> Task<Message> {
+ match key {
+ Key::Named(Named::ArrowDown) => {
+ self.cursor.0 += 1;
+ self.cursor_visible = true;
+ }
+ Key::Named(Named::ArrowUp) => {
+ self.cursor.0 = usize::saturating_sub(self.cursor.0, 1);
+ self.cursor_visible = true;
+ }
+ Key::Named(Named::ArrowRight) => {
+ self.cursor.1 += 1;
+ self.cursor_visible = true;
+ }
+ Key::Named(Named::ArrowLeft) => {
+ self.cursor.1 = usize::saturating_sub(self.cursor.1, 1);
+ self.cursor_visible = true;
+ }
+ _ => {}
+ }
Task::none()
}
@@ -46,18 +66,19 @@ impl Editor {
Task::none()
}
- pub fn view(&self) -> EditorWidget<'_> {
- EditorWidget::new(self)
+ pub fn view(&self, glyph_size: Size) -> EditorWidget<'_> {
+ EditorWidget::new(self, glyph_size)
}
}
pub struct EditorWidget<'a> {
editor: &'a Editor,
+ glyph_size: Size,
}
impl<'a> EditorWidget<'a> {
- fn new(editor: &'a Editor) -> EditorWidget<'a> {
- Self { editor }
+ fn new(editor: &'a Editor, glyph_size: Size) -> EditorWidget<'a> {
+ Self { editor, glyph_size }
}
}
@@ -111,16 +132,29 @@ where
wrapping: Wrapping::None,
};
- eprintln!("layout bounds {:?}", layout.bounds());
- eprintln!("content: {:?}", &self.editor.content[0..20]);
- eprintln!("font: {:?}", text.font);
-
renderer.fill_text(
text,
layout.bounds().position() + [10.0, 10.0].into(),
Color::WHITE,
layout.bounds(),
);
+
+ if self.editor.cursor_visible {
+ renderer.fill_quad(
+ Quad {
+ bounds: Rectangle::new(
+ (
+ 10.0 + self.glyph_size.width * self.editor.cursor.1 as f32,
+ 10.0 + self.glyph_size.height * self.editor.cursor.0 as f32,
+ )
+ .into(),
+ self.glyph_size,
+ ),
+ ..Default::default()
+ },
+ Color::from_rgba(0.6, 1.0, 0.7, 0.4),
+ );
+ }
}
}
@@ -133,3 +167,30 @@ where
Self::new(editor_widget)
}
}
+
+// FontSystem::new loads all system fonts!
+pub fn measure_glyph(font_family_name: &str, font_size: f32, char: &str) -> Size {
+ use iced::advanced::graphics::text::cosmic_text::{
+ Attrs, Buffer, Family, FontSystem, Metrics, Shaping,
+ };
+ let line_height_scale = LineHeight::default();
+ let line_height = line_height_scale.to_absolute(font_size.into()).0;
+
+ let mut font_system = FontSystem::new();
+ let metrics = Metrics {
+ font_size: font_size,
+ line_height,
+ };
+ let mut buffer = Buffer::new_empty(metrics);
+ let attrs = Attrs::new().family(Family::Name(font_family_name));
+ buffer.set_text(&mut font_system, char, attrs, Shaping::Advanced);
+
+ let width = buffer
+ .layout_runs()
+ .fold(0.0, |width, run| run.line_w.max(width));
+
+ Size {
+ width,
+ height: buffer.metrics().line_height,
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index b0cf925..85542e7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,36 +2,36 @@ use anyhow::Result;
use iced::Length::Fill;
use iced::keyboard::{Key, Modifiers};
use iced::widget::{button, column, container, row, text};
-use iced::{Element, Font, Subscription, Task, Theme, border, keyboard, time};
+use iced::{Element, Font, Size, Subscription, Task, Theme, keyboard, time};
use crate::editor::Editor;
mod editor;
+const FONT_FAMILY_NAME: &str = "Iosevka Term Slab";
+const FONT_SIZE: f32 = 14.0;
+
fn main() -> Result<()> {
let argv = std::env::args().skip(1).collect::<Vec<_>>();
+
+ let glyph_size = editor::measure_glyph(FONT_FAMILY_NAME, FONT_SIZE, "x");
+ let state_builder = StateBuilder::with_glyph_size(glyph_size);
let init_state = if argv.len() == 1 {
- State::from_filename(&argv[0])?
+ state_builder.build_with_filename(&argv[0])?
} else {
- State::default()
+ state_builder.build_untitled()
};
- let app = iced::application(title, update, view)
+ iced::application(title, update, view)
.theme(|_| Theme::CatppuccinMocha)
- .subscription(subscription);
-
- let font = std::fs::read("font.ttf").ok();
- let app = match font {
- Some(bytes) => app.font(bytes).default_font(Font {
- family: iced::font::Family::Name("Iosevka Term Slab"),
+ .subscription(subscription)
+ .default_font(Font {
+ family: iced::font::Family::Name(FONT_FAMILY_NAME),
weight: iced::font::Weight::Normal,
stretch: iced::font::Stretch::Normal,
style: iced::font::Style::Normal,
- }),
- None => app,
- };
-
- app.run_with(|| (init_state, Task::none()))?;
+ })
+ .run_with(|| (init_state, Task::none()))?;
Ok(())
}
@@ -45,28 +45,48 @@ fn title(state: &State) -> String {
fn subscription(_state: &State) -> Subscription<Message> {
Subscription::batch(vec![
- time::every(time::Duration::from_secs(1)).map(|_instant| Message::ToggleCursorVisibility),
+ time::every(time::Duration::from_millis(500))
+ .map(|_instant| Message::ToggleCursorVisibility),
keyboard::on_key_press(|k, m| Some(Message::KeyPress(k, m))),
])
}
-#[derive(Default)]
-struct State {
- filename: Option<String>,
- editor: Editor,
+struct StateBuilder {
+ glyph_size: Size,
}
-impl State {
- fn from_filename(filename: &str) -> Result<Self> {
+impl StateBuilder {
+ fn with_glyph_size(glyph_size: Size) -> Self {
+ Self { glyph_size }
+ }
+
+ fn build_untitled(self) -> State {
+ State {
+ glyph_size: self.glyph_size,
+ ..Default::default()
+ }
+ }
+
+ fn build_with_filename(self, filename: &str) -> Result<State> {
let content = std::fs::read_to_string(filename)?;
let filename = std::path::absolute(filename)?;
- Ok(Self {
+ Ok(State {
filename: Some(filename.to_string_lossy().to_string()),
editor: Editor::with_content(content),
+ glyph_size: self.glyph_size,
})
}
}
+#[derive(Default)]
+struct State {
+ filename: Option<String>,
+ editor: Editor,
+ glyph_size: Size,
+}
+
+impl State {}
+
#[derive(Debug, Clone)]
enum Message {
KeyPress(Key, Modifiers),
@@ -99,5 +119,5 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
}
fn view(state: &State) -> Element<'_, Message> {
- state.editor.view().into()
+ state.editor.view(state.glyph_size).into()
}