Compare commits

...

10 commits

Author SHA1 Message Date
3626c7e55d
Link to forgejo dep 2024-03-18 18:31:38 -04:00
StratusFearMe21
206aa971e2
Merge pull request #2 from torokati44/master
Switch to mainline glyphon 0.5.0
2024-03-07 02:59:32 +00:00
StratusFearMe21
3d39260c53
Create LICENSE 2024-03-07 02:47:00 +00:00
TÖRÖK Attila
0cd1588a0e Switch to mainline glyphon 0.5.0 2024-03-07 01:34:52 +01:00
469adc63a4
Fix example 2024-03-06 18:04:23 -05:00
4da780b8f2
Use non-git egui (Fix #1) 2024-03-06 17:57:51 -05:00
11f0443321
Use non-git egui 2024-03-06 17:56:07 -05:00
719e904831
Add example 2024-02-05 11:30:20 -05:00
c635390455
Fix deps 2024-02-05 11:19:45 -05:00
03ad1aa499
Fix deps 2024-02-05 11:13:50 -05:00
5 changed files with 2802 additions and 179 deletions

2757
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
egui = { git = "https://github.com/emilk/egui", version = "0.25.0" }
egui-wgpu = { git = "https://github.com/emilk/egui", version = "0.25.0" }
glyphon = { git = "https://github.com/StratusFearMe21/glyphon" }
egui = { version = "0.26.2", default-features = false }
egui-wgpu = "0.26.2"
glyphon = { git = "https://git.nations.lol/fnmain/glyphon.git" }
[dev-dependencies]
cosmic-jotdown = { path = "../cosmic-jotdown" }
eframe = { version = "0.26.2", features = ["wgpu"] }
env_logger = "0.11.3"

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Isaac Mills
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

128
examples/hello-world.rs Normal file
View file

@ -0,0 +1,128 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use std::{ops::Deref, sync::Arc};
use eframe::{
egui::{self, Slider},
egui_wgpu,
epaint::{
mutex::{Mutex, RwLock},
Rect, Vec2,
},
CreationContext,
};
use egui::{Align2, Pos2};
use egui_glyphon::{
glyphon::{Attrs, Family, FontSystem, Metrics, Shaping},
BufferWithTextArea, GlyphonRenderer, GlyphonRendererCallback,
};
use glyphon::{cosmic_text::Align, Buffer};
fn main() -> Result<(), eframe::Error> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
..Default::default()
};
eframe::run_native(
"My egui App",
options,
Box::new(|cc| Box::new(MyApp::new(cc))),
)
}
struct MyApp {
font_system: Arc<Mutex<FontSystem>>,
buffers: Vec<Arc<RwLock<Buffer>>>,
}
impl Default for MyApp {
fn default() -> Self {
let mut font_system = FontSystem::new();
let buffers = cosmic_jotdown::jotdown_into_buffers(
r#"# Header
Test _test_ *test* 'test' "`test`""#,
&mut font_system,
Metrics::new(30.0, 42.0),
f32::MAX,
)
.map(|buffer| Arc::new(RwLock::new(buffer)))
.collect();
Self {
font_system: Arc::new(Mutex::new(font_system)),
buffers,
}
}
}
impl MyApp {
fn new(cc: &CreationContext<'_>) -> Self {
let app = Self::default();
if let Some(ref wgpu) = cc.wgpu_render_state {
GlyphonRenderer::insert(wgpu, Arc::clone(&app.font_system));
}
app
}
}
pub fn measure_buffer(buffer: &Buffer, vb: Vec2) -> Rect {
let mut rtl = false;
let (width, total_lines) =
buffer
.layout_runs()
.fold((0.0, 0usize), |(width, total_lines), run| {
if run.rtl {
rtl = true;
}
(run.line_w.max(width), total_lines + 1)
});
let (max_width, max_height) = buffer.size();
let size = Vec2::new(
if rtl { vb.x } else { width.min(max_width) },
(total_lines as f32 * buffer.metrics().line_height).min(max_height),
);
match buffer.lines[0].align() {
Some(Align::Right) | Some(Align::End) => {
Align2::RIGHT_TOP.align_size_within_rect(size, Rect::from_min_size(Pos2::ZERO, vb))
}
Some(Align::Center) | Some(Align::Justified) => {
Align2::CENTER_TOP.align_size_within_rect(size, Rect::from_min_size(Pos2::ZERO, vb))
}
Some(Align::Left) | None => Rect::from_min_size(Pos2::ZERO, size),
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
let mut rect = Rect::from_min_size(ui.cursor().min, Vec2::INFINITY);
let buffers: Vec<BufferWithTextArea> = self
.buffers
.iter()
.cloned()
.map(|buffer| {
let buffer_size = measure_buffer(buffer.read().deref(), ui.max_rect().size());
let this_rect = rect;
rect.min.y += buffer_size.height() + 48.0;
BufferWithTextArea::new(
buffer,
this_rect,
1.0,
egui_glyphon::glyphon::Color::rgb(255, 255, 255),
ui.ctx(),
)
})
.collect();
ui.painter().add(egui_wgpu::Callback::new_paint_callback(
ui.max_rect(),
GlyphonRendererCallback { buffers },
));
});
}
}

View file

@ -1,12 +1,12 @@
//! This crate is for using [`glyphon`] to render advanced shaped text to the screen in an [`egui`] application
//! Please see the example for a primer on how to use this crate
use std::ops::{Deref, DerefMut};
use std::ops::DerefMut;
use std::sync::Arc;
use egui::mutex::{Mutex, RwLock};
use egui::{Pos2, Rect, Vec2};
use egui_wgpu::renderer::ScreenDescriptor;
use egui_wgpu::wgpu;
use egui_wgpu::ScreenDescriptor;
use glyphon::{
Buffer, Color, ColorMode, FontSystem, PrepareError, RenderError, Resolution, SwashCache,
TextArea, TextAtlas, TextBounds, TextRenderer,
@ -15,8 +15,8 @@ use glyphon::{
pub use glyphon;
/// A text buffer with some accosiated data used to construect a [`glyphon::TextArea`]
pub struct BufferWithTextArea<T: AsRef<Buffer> + Send + Sync> {
pub buffer: Arc<RwLock<T>>,
pub struct BufferWithTextArea {
pub buffer: Arc<RwLock<Buffer>>,
pub rect: Rect,
pub scale: f32,
pub opacity: f32,
@ -47,9 +47,9 @@ pub fn measure_buffer(buffer: &Buffer) -> Rect {
)
}
impl<T: AsRef<Buffer> + Send + Sync + 'static> BufferWithTextArea<T> {
impl BufferWithTextArea {
pub fn new(
buffer: Arc<RwLock<T>>,
buffer: Arc<RwLock<Buffer>>,
rect: Rect,
opacity: f32,
default_color: Color,
@ -77,10 +77,7 @@ pub struct GlyphonRenderer {
impl GlyphonRenderer {
/// Insert an instance of itself into the [`egui_wgpu::RenderState`]
pub fn insert<'a>(
wgpu_render_state: &'a egui_wgpu::RenderState,
font_system: Arc<Mutex<FontSystem>>,
) {
pub fn insert(wgpu_render_state: &egui_wgpu::RenderState, font_system: Arc<Mutex<FontSystem>>) {
let device = &wgpu_render_state.device;
let queue = &wgpu_render_state.queue;
@ -89,7 +86,7 @@ impl GlyphonRenderer {
device,
queue,
wgpu_render_state.target_format,
ColorMode::Egui,
ColorMode::Web,
);
let text_renderer =
TextRenderer::new(&mut atlas, device, wgpu::MultisampleState::default(), None);
@ -106,12 +103,12 @@ impl GlyphonRenderer {
});
}
fn prepare<A: AsRef<Buffer>, T: Deref<Target = A>>(
fn prepare<'a>(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
screen_resolution: Resolution,
text_areas: impl IntoIterator<Item = TextArea<A, T>>,
text_areas: impl IntoIterator<Item = TextArea<'a>>,
) -> Result<(), PrepareError> {
self.text_renderer.prepare(
device,
@ -132,12 +129,12 @@ impl GlyphonRenderer {
/// A callback which can be put into an [`egui_wgpu::renderer::Callback`].
// And wrapped with an [`egui::PaintCallback`]. Only add one callback per individual
// deffered viewport.
pub struct GlyphonRendererCallback<T: AsRef<Buffer> + Send + Sync> {
pub struct GlyphonRendererCallback {
/// These buffers will be rendered to the screen all at the same time on the same layer.
pub buffers: Vec<BufferWithTextArea<T>>,
pub buffers: Vec<BufferWithTextArea>,
}
impl<T: AsRef<Buffer> + Send + Sync> egui_wgpu::CallbackTrait for GlyphonRendererCallback<T> {
impl egui_wgpu::CallbackTrait for GlyphonRendererCallback {
fn prepare(
&self,
device: &wgpu::Device,
@ -148,6 +145,26 @@ impl<T: AsRef<Buffer> + Send + Sync> egui_wgpu::CallbackTrait for GlyphonRendere
) -> Vec<wgpu::CommandBuffer> {
let glyphon_renderer: &mut GlyphonRenderer = resources.get_mut().unwrap();
glyphon_renderer.atlas.trim();
let bufrefs: Vec<_> = self.buffers.iter().map(|b| b.buffer.read()).collect();
let text_areas: Vec<_> = self
.buffers
.iter()
.enumerate()
.map(|(i, b)| TextArea {
buffer: bufrefs.get(i).unwrap(),
left: b.rect.left(),
top: b.rect.top(),
scale: b.scale,
bounds: TextBounds {
left: b.rect.left() as i32,
top: b.rect.top() as i32,
right: b.rect.right() as i32,
bottom: b.rect.bottom() as i32,
},
default_color: b.default_color,
opacity: b.opacity,
})
.collect();
glyphon_renderer
.prepare(
device,
@ -156,20 +173,7 @@ impl<T: AsRef<Buffer> + Send + Sync> egui_wgpu::CallbackTrait for GlyphonRendere
width: screen_descriptor.size_in_pixels[0],
height: screen_descriptor.size_in_pixels[1],
},
self.buffers.iter().map(|b| TextArea {
buffer: b.buffer.read(),
left: b.rect.left(),
top: b.rect.top(),
scale: b.scale,
opacity: b.opacity,
bounds: TextBounds {
left: b.rect.left() as i32,
top: b.rect.top() as i32,
right: b.rect.right() as i32,
bottom: b.rect.bottom() as i32,
},
default_color: b.default_color,
}),
text_areas,
)
.unwrap();
Vec::new()