Skip to content
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ minimal = ["wayland", "vulkan", "dx12", "metal"]
# Minimal + strongly recommended features
default = ["minimal", "clipboard", "shaping"]
# All standard test target features
stable = ["default", "view", "image-default-formats", "canvas", "svg", "markdown", "spawn", "x11", "toml", "yaml", "json", "ron", "macros_log"]
stable = ["default", "view", "image-default-formats", "canvas", "svg", "markdown", "spawn", "x11", "toml", "yaml", "json", "ron", "macros_log", "parley"]
# Enables all "recommended" features for nightly rustc
nightly = ["stable", "nightly-diagnostics", "kas-core/nightly"]
# Additional, less recommendation-worthy features
Expand All @@ -56,6 +56,9 @@ spec = ["kas-core/spec"]
# Enable view widgets
view = ["dep:kas-view"]

# Enable Parley text backend
parley = ["kas-core/parley", "kas-wgpu?/parley"]

#Enable WGPU backend:
wgpu = ["dep:kas-wgpu"]

Expand Down
10 changes: 9 additions & 1 deletion crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ rustdoc-args = ["--cfg", "docsrs"]
# The minimal feature set needed to build basic applications (with assumptions
# about target platforms).
minimal = ["wayland"]
# HACK
default = ["minimal", "parley"]
# All standard test target features
stable = ["minimal", "clipboard", "markdown", "spawn", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log"]
# Enables all "recommended" features for nightly rustc
nightly = ["stable", "nightly-diagnostics"]
nightly = ["stable", "nightly-diagnostics", "serde"]
# Additional, less recommendation-worthy features
experimental = ["dark-light", "unsafe_node"]

Expand Down Expand Up @@ -82,6 +84,9 @@ dark-light = ["dep:dark-light"]
# Support spawning async tasks
spawn = ["dep:async-global-executor"]

# Enable Parley text backend
parley = ["dep:parley"]

# Optimize Node using unsafe code
unsafe_node = []

Expand All @@ -106,8 +111,11 @@ async-global-executor = { version = "3.1.0", optional = true }
cfg-if = "1.0.0"
smol_str = "0.3.2"
hash_hasher = "2.0.4"
const-fnv1a-hash = "1.1.0"
accesskit = { version = "0.21.0", optional = true }
accesskit_winit = { version = "0.29.0", optional = true }
parley = { version = "0.6", optional = true }
const-fnv1a-hash = "1.1.0"

[target.'cfg(any(target_os="linux", target_os="dragonfly", target_os="freebsd", target_os="netbsd", target_os="openbsd"))'.dependencies]
smithay-clipboard = { version = "0.7.0", optional = true }
Expand Down
26 changes: 24 additions & 2 deletions crates/kas-core/src/config/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

use crate::ConfigAction;
use crate::text::fonts::FontSelector;
use crate::text::class;
use crate::theme::TextClass;
use std::borrow::Cow;
use std::collections::BTreeMap;

/// A message which may be used to update [`FontConfig`]
Expand All @@ -31,9 +33,15 @@ pub struct FontConfig {
/// Standard fonts
///
/// TODO: read/write support.
#[cfg_attr(feature = "serde", serde(skip, default))]
#[cfg_attr(feature = "serde", serde(skip, default = "defaults::fonts"))]
pub fonts: BTreeMap<TextClass, FontSelector>,

/// Standard font for each text class
///
/// Accepts a font family specifier in CSS format for each text class key.
#[cfg_attr(feature = "serde", serde(default))]
pub class_fonts: BTreeMap<class::Key, Cow<'static, str>>,

/// Text glyph rastering settings
#[cfg_attr(feature = "serde", serde(default))]
pub raster: RasterConfig,
Expand Down Expand Up @@ -172,12 +180,26 @@ mod defaults {
}

pub fn fonts() -> BTreeMap<TextClass, FontSelector> {
let sans_serifrif = FamilySelector::SANS_SERIF;
let serif = FamilySelector::SERIF;
let list = [
(TextClass::Label(false), sans_serif.into()),
(TextClass::Label(true), sans_serif.into()),
(TextClass::LabelScroll, sans_serif.into()),
(TextClass::Edit(false), serif.into()),
(TextClass::Edit(true), serif.into()),
];
list.iter().cloned().collect()
list.into_iter().collect()
}

pub fn class_fonts() -> BTreeMap<class::Key, Cow<'static, str>> {
let ui = "system-ui, ui-sans-serif, sans-serif";
let serif = "ui-serif, serif";
let list = [
(<class::Label as class::TextClass>::KEY, ui.into()),
(<class::EditField as class::TextClass>::KEY, serif.into()),
];
list.into_iter().collect()
}

pub fn mode() -> u8 {
Expand Down
21 changes: 21 additions & 0 deletions crates/kas-core/src/draw/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ pub trait Draw {
/// Draw the image in the given `rect`
fn image(&mut self, id: ImageId, rect: Quad);

/// Draw Parley text
#[cfg(feature = "parley")]
fn parley_run(
&mut self,
rect: Quad,
col: Rgba,
run: &parley::layout::GlyphRun<'_, crate::theme::TextBrush>,
);

/// Draw text with a colour
///
/// Text is drawn from `pos` and clipped to `bounding_box`.
Expand Down Expand Up @@ -278,6 +287,18 @@ impl<'a, DS: DrawSharedImpl> Draw for DrawIface<'a, DS> {
self.shared.draw.draw_image(self.draw, self.pass, id, rect);
}

#[cfg(feature = "parley")]
fn parley_run(
&mut self,
rect: Quad,
col: Rgba,
run: &parley::layout::GlyphRun<'_, crate::theme::TextBrush>,
) {
self.shared
.draw
.parley_run(self.draw, self.pass, rect, col, run);
}

fn text(&mut self, pos: Vec2, bb: Quad, text: &TextDisplay, col: Rgba) {
self.shared
.draw
Expand Down
11 changes: 11 additions & 0 deletions crates/kas-core/src/draw/draw_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,17 @@ pub trait DrawSharedImpl: Any {
/// Draw the image in the given `rect`
fn draw_image(&self, draw: &mut Self::Draw, pass: PassId, id: ImageId, rect: Quad);

/// Draw Parley text
#[cfg(feature = "parley")]
fn parley_run(
&mut self,
draw: &mut Self::Draw,
pass: PassId,
rect: Quad,
col: Rgba,
run: &parley::layout::GlyphRun<'_, crate::theme::TextBrush>,
);

/// Draw text with a colour
fn draw_text(
&mut self,
Expand Down
29 changes: 27 additions & 2 deletions crates/kas-core/src/event/config_cx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@

use crate::event::EventState;
use crate::text::format::FormattableText;
use crate::theme::{SizeCx, Text, ThemeSize};
use crate::theme::{SizeCx, Text, ThemeSize, TextBrush};
use crate::{ActionResize, Id, Node};
use crate::runner::ParleyContext;
use std::any::TypeId;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
Expand All @@ -22,6 +23,7 @@ use std::ops::{Deref, DerefMut};
#[must_use]
pub struct ConfigCx<'a> {
pub(super) theme: &'a dyn ThemeSize,
pub(super) parley: &'a mut ParleyContext,
pub(crate) state: &'a mut EventState,
pub(crate) resize: ActionResize,
pub(crate) redraw: bool,
Expand All @@ -31,9 +33,11 @@ impl<'a> ConfigCx<'a> {
/// Construct
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
pub fn new(sh: &'a dyn ThemeSize, ev: &'a mut EventState) -> Self {
pub fn new(sh: &'a dyn ThemeSize,
parley: &'a mut ParleyContext, ev: &'a mut EventState) -> Self {
ConfigCx {
theme: sh,
parley,
state: ev,
resize: ActionResize(false),
redraw: false,
Expand Down Expand Up @@ -80,6 +84,27 @@ impl<'a> ConfigCx<'a> {
self.resize |= start_resize;
}

/// Create a ranged style builder for a [`parley::Layout`]
pub fn ranged_builder<'b>(&'b mut self, text: &'b str) -> parley::RangedBuilder<'b, TextBrush> {
let scale = self.config.scale_factor();
let quantize = true;
self.pcx
.lcx
.ranged_builder(&mut self.pcx.fcx, text, scale, quantize)
}

/// Create a tree style builder for a [`parley::Layout`]
pub fn tree_builder<'b>(
&'b mut self,
raw_style: &parley::style::TextStyle<'_, TextBrush>,
) -> parley::TreeBuilder<'b, TextBrush> {
let scale = self.config.scale_factor();
let quantize = true;
self.pcx
.lcx
.tree_builder(&mut self.pcx.fcx, scale, quantize, raw_style)
}

/// Configure a text object
///
/// This selects a font given the [`TextClass`][crate::theme::TextClass],
Expand Down
3 changes: 3 additions & 0 deletions crates/kas-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ extern crate self as kas;

#[doc(inline)] pub extern crate easy_cast as cast;

// TODO: this is temporary
pub extern crate parley;

// internal modules:
#[cfg(feature = "accesskit")] pub(crate) mod accesskit;
mod action;
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/runner/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ where
win_id = id;
}
if let Some(window) = self.windows.get_mut(&win_id) {
window.send_close(target);
window.send_close(&mut self.state, target);
}
}
Pending::Exit => close_all = true,
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use crate::ConfigAction;
use crate::messages::Erased;
use crate::window::{BoxedWindow, PopupDescriptor, WindowId};
use event_loop::Loop;
pub(crate) use shared::RunnerT;
use shared::Shared;
use std::fmt::Debug;
pub use window::Window;
pub(crate) use shared::{ParleyContext, RunnerT};
pub(crate) use window::WindowDataErased;

pub use common::{Error, Platform, Result};
Expand Down
17 changes: 16 additions & 1 deletion crates/kas-core/src/runner/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::{
use crate::config::Config;
use crate::draw::{DrawShared, DrawSharedImpl, SharedState};
use crate::messages::Erased;
use crate::theme::Theme;
use crate::theme::{TextBrush, Theme};
#[cfg(feature = "clipboard")]
use crate::util::warn_about_error;
use crate::window::{PopupDescriptor, Window as WindowWidget, WindowId, WindowIdFactory};
Expand All @@ -25,6 +25,12 @@ use std::task::Waker;

#[cfg(feature = "clipboard")] use arboard::Clipboard;

#[derive(Default)]
pub(crate) struct ParleyContext {
pub(crate) fcx: parley::FontContext,
pub(crate) lcx: parley::LayoutContext<TextBrush>,
}

/// Runner state shared by all windows and used by [`RunnerT`]
pub(super) struct Shared<Data: AppData, G: GraphicsInstance, T: Theme<G::Shared>> {
pub(super) platform: Platform,
Expand All @@ -35,6 +41,7 @@ pub(super) struct Shared<Data: AppData, G: GraphicsInstance, T: Theme<G::Shared>
pub(super) instance: G,
pub(super) draw: Option<SharedState<G::Shared>>,
pub(super) theme: T,
pub(super) parley: ParleyContext,
pub(super) messages: MessageStack,
pub(super) pending: VecDeque<Pending<Data>>,
pub(super) send_queue: VecDeque<(Id, Erased)>,
Expand Down Expand Up @@ -77,6 +84,7 @@ where
instance,
draw: None,
theme,
parley: ParleyContext::default(),
messages: MessageStack::new(),
pending: Default::default(),
send_queue: Default::default(),
Expand Down Expand Up @@ -238,6 +246,9 @@ pub(crate) trait RunnerT {
/// Access the [`DrawShared`] object
fn draw_shared(&mut self) -> &mut dyn DrawShared;

/// Access the [`ParleyContext`]
fn parley_context(&mut self) -> &mut ParleyContext;

/// Access a Waker
fn waker(&self) -> &std::task::Waker;
}
Expand Down Expand Up @@ -384,6 +395,10 @@ impl<Data: AppData, G: GraphicsInstance, T: Theme<G::Shared>> RunnerT for Shared
self.draw.as_mut().unwrap()
}

fn parley_context(&mut self) -> &mut ParleyContext {
&mut self.parley
}

#[inline]
fn waker(&self) -> &std::task::Waker {
&self.waker
Expand Down
10 changes: 6 additions & 4 deletions crates/kas-core/src/runner/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ impl<A: AppData, G: GraphicsInstance, T: Theme<G::Shared>> Window<A, G, T> {
let mut theme = shared.theme.new_window(config);

let mut node = self.widget.as_node(data);
let _: ActionResize = self.ev_state.full_configure(theme.size(), node.re());
let _: ActionResize = self.ev_state.full_configure(theme.size(), node.re(),
&mut shared.parley);


let mut cx = SizeCx::new(&mut self.ev_state, theme.size());
let mut solve_cache = SolveCache::default();
solve_cache.find_constraints(node, &mut cx);
Expand Down Expand Up @@ -526,7 +528,7 @@ impl<A: AppData, G: GraphicsInstance, T: Theme<G::Shared>> Window<A, G, T> {
self.widget.add_popup(&mut cx, data, id, popup);
}

pub(super) fn send_close(&mut self, id: WindowId) {
pub(super) fn send_close(&mut self, shared: &mut Shared<A, G, T>, id: WindowId) {
if id == self.ev_state.window_id {
self.ev_state.close_own_window();
} else if let Some((ref theme, _)) = self.theme_and_window {
Expand Down Expand Up @@ -555,14 +557,14 @@ impl<A: AppData, G: GraphicsInstance, T: Theme<G::Shared>> Window<A, G, T> {
log::trace!(target: "kas_perf::wgpu::window", "reconfigure: {}µs", time.elapsed().as_micros());
}

pub(super) fn update(&mut self, data: &A) {
pub(super) fn update(&mut self, shared: &mut Shared<A, G, T>, data: &A) {
let time = Instant::now();
let Some((ref theme, ref mut window)) = self.theme_and_window else {
return;
};

let size = theme.size();
let mut cx = ConfigCx::new(&size, &mut self.ev_state);
let mut cx = ConfigCx::new(&mut self.ev_state, &mut shared.parley, size);
cx.update(self.widget.as_node(data));
if *cx.resize {
self.apply_size(data, false, true);
Expand Down
Loading
Loading