Newer
Older
use crate::*;
use bevy::{prelude::*, window::PrimaryWindow};
use cosmic_text::Affinity;
/// System set for cosmic text layout systems. Runs in [`PostUpdate`]
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub struct WidgetSet;
pub(crate) struct WidgetPlugin;
impl Plugin for WidgetPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, reshape.in_set(WidgetSet).after(InputSet))
.add_systems(
PostUpdate,
(
(new_image_from_default, set_sprite_size_from_ui),
set_widget_size,
set_buffer_size,
set_padding,
set_x_offset,
)
.chain()
.in_set(WidgetSet)
.after(TransformSystem::TransformPropagate),
);
}
}
/// Wrapper for a [`Vec2`] describing the horizontal and vertical padding of a widget.
/// This is set programatically, not for user modification.
/// To set a widget's padding, use [`CosmicTextAlign`]
#[derive(Component, Default, Deref, DerefMut, Debug)]
/// Wrapper for a [`Vec2`] describing the horizontal and vertical size of a widget.
/// This is set programatically, not for user modification.
/// To set a widget's size, use either it's [`Sprite`] dimensions or modify the target UI element's
/// size.
#[derive(Component, Default, Deref, DerefMut)]
/// Reshapes text in a [`CosmicEditor`]
fn reshape(mut query: Query<&mut CosmicEditor>, mut font_system: ResMut<CosmicFontSystem>) {
for mut cosmic_editor in query.iter_mut() {
cosmic_editor.shape_as_needed(&mut font_system.0, false);
}
}
/// Programatically sets the [`CosmicPadding`] of a widget based on it's [`CosmicTextAlign`]
fn set_padding(
&CosmicBuffer,
&CosmicWidgetSize,
Option<&CosmicEditor>,
),
Or<(
With<CosmicEditor>,
Changed<CosmicBuffer>,
Changed<CosmicWidgetSize>,
)>,
>,
) {
for (mut padding, position, buffer, size, editor_opt) in query.iter_mut() {
// TODO: At least one of these clones is uneccessary
let mut buffer = buffer.0.clone();
if let Some(editor) = editor_opt {
buffer = editor.with_buffer(|b| b.clone());
}
if !buffer.redraw() {
continue;
}
padding.0 = match position {
CosmicTextAlign::Center { padding: _ } => Vec2::new(
get_x_offset_center(size.0.x, &buffer) as f32,
get_y_offset_center(size.0.y, &buffer) as f32,
),
CosmicTextAlign::TopLeft { padding } => Vec2::new(*padding as f32, *padding as f32),
CosmicTextAlign::Left { padding } => Vec2::new(
*padding as f32,
get_y_offset_center(size.0.y, &buffer) as f32,
),
}
}
}
/// Programatically sets the [`CosmicWidgetSize`] of a widget based on it's [`Sprite`] properties
fn set_widget_size(
mut query: Query<(&mut CosmicWidgetSize, &Sprite), Changed<Sprite>>,
windows: Query<&Window, With<PrimaryWindow>>,
) {
if windows.iter().len() == 0 {
return;
}
// TODO: early return if sprite size is unchanged
let scale = windows.single().scale_factor();
for (mut size, sprite) in query.iter_mut() {
size.0 = sprite.custom_size.unwrap().ceil() * scale;
}
}
/// Sets the internal [`Buffer`]'s size according to the [`CosmicWidgetSize`] and [`CosmicTextAlign`]
fn set_buffer_size(
)>,
>,
mut font_system: ResMut<CosmicFontSystem>,
) {
for (mut buffer, mode, size, position) in query.iter_mut() {
let padding_x = match position {
CosmicTextAlign::Center { padding: _ } => 0.,
CosmicTextAlign::TopLeft { padding } => *padding as f32,
CosmicTextAlign::Left { padding } => *padding as f32,
};
let (buffer_width, buffer_height) = match mode {
CosmicWrap::InfiniteLine => (f32::MAX, size.0.y),
CosmicWrap::Wrap => (size.0.x - padding_x, size.0.y),
};
buffer.set_size(&mut font_system.0, buffer_width, buffer_height);
}
}
/// Instantiates a new image for a [`CosmicBuffer`]
fn new_image_from_default(
mut query: Query<&mut Handle<Image>, Added<CosmicBuffer>>,
mut images: ResMut<Assets<Image>>,
) {
for mut canvas in query.iter_mut() {
*canvas = images.add(Image::default());
}
}
for (mut x_offset, mode, editor, size, position) in query.iter_mut() {
if mode != &CosmicWrap::InfiniteLine {
let cursor = editor.cursor();
if let Some(line) = editor.with_buffer(|b| b.clone()).layout_runs().next() {
for (idx, glyph) in line.glyphs.iter().enumerate() {
if cursor.affinity == Affinity::Before {
if idx <= cursor.index {
cursor_x += glyph.w;
} else {
break;
}
} else if idx < cursor.index {
cursor_x += glyph.w;
} else {
break;
CosmicTextAlign::Center { padding } => *padding as f32,
CosmicTextAlign::TopLeft { padding } => *padding as f32,
CosmicTextAlign::Left { padding } => *padding as f32,
};
if x_offset.width == 0. {
x_offset.width = size.x - padding_x * 2.;
let right = x_offset.width + x_offset.left;
if cursor_x > right {
let diff = cursor_x - right;
x_offset.left += diff;
}
if cursor_x < x_offset.left {
let diff = x_offset.left - cursor_x;
x_offset.left -= diff;
fn set_sprite_size_from_ui(
mut source_q: Query<&mut Sprite, With<CosmicBuffer>>,
dest_q: Query<(&Node, &CosmicSource), Changed<Node>>,
) {
for (node, source) in dest_q.iter() {
if let Ok(mut sprite) = source_q.get_mut(source.0) {
sprite.custom_size = Some(node.size().ceil().max(Vec2::ONE));
}
}
}