diff --git a/examples/basic_sprite.rs b/examples/basic_sprite.rs index 29b8b93c39573cb4d4fcdfb0b9c96e1571c5a4d9..e2befd82470db50e402f85fc858a83134fbef4dd 100644 --- a/examples/basic_sprite.rs +++ b/examples/basic_sprite.rs @@ -20,26 +20,24 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { let scale_factor = primary_window.scale_factor() as f32; - let cosmic_edit = ( - CosmicEditBundle { - metrics: CosmicMetrics { - font_size: 14., - line_height: 18., - scale_factor, - }, - text_position: CosmicTextPosition::Center, - attrs: CosmicAttrs(AttrsOwned::new(attrs)), - text_setter: CosmicText::OneStyle("馃榾馃榾馃榾 x => y".to_string()), - ..default() + let cosmic_edit = (CosmicEditBundle { + metrics: CosmicMetrics { + font_size: 14., + line_height: 18., + scale_factor, }, - SpriteBundle { + text_position: CosmicTextPosition::Center, + attrs: CosmicAttrs(AttrsOwned::new(attrs)), + text_setter: CosmicText::OneStyle("馃榾馃榾馃榾 x => y".to_string()), + sprite_bundle: SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::new(primary_window.width(), primary_window.height())), ..default() }, ..default() }, - ); + ..default() + },); let cosmic_edit = commands.spawn(cosmic_edit).id(); diff --git a/examples/basic_ui.rs b/examples/basic_ui.rs index 1688f79c194544722144347ce889810f07ba33fc..50c90082bf3502c6bd8990b0ee99fdcebc928831 100644 --- a/examples/basic_ui.rs +++ b/examples/basic_ui.rs @@ -1,7 +1,7 @@ use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*, window::PrimaryWindow}; use bevy_cosmic_edit::{ AttrsOwned, CosmicAttrs, CosmicEditBundle, CosmicEditPlugin, CosmicEditor, CosmicFontConfig, - CosmicMetrics, CosmicText, CosmicTextPosition, Focus, + CosmicMetrics, CosmicSource, CosmicText, CosmicTextPosition, Focus, }; fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { @@ -20,8 +20,8 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { let scale_factor = primary_window.scale_factor() as f32; - let cosmic_edit = ( - CosmicEditBundle { + let cosmic_edit = commands + .spawn(CosmicEditBundle { metrics: CosmicMetrics { font_size: 14., line_height: 18., @@ -31,21 +31,28 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { attrs: CosmicAttrs(AttrsOwned::new(attrs)), text_setter: CosmicText::OneStyle("馃榾馃榾馃榾 x => y".to_string()), ..default() - }, - // Use buttonbundle for layout - ButtonBundle { - style: Style { - width: Val::Percent(100.), - height: Val::Percent(100.), + }) + .id(); + + commands + .spawn( + // Use buttonbundle for layout + // Includes Interaction and UiImage which are used by the plugin. + ButtonBundle { + style: Style { + width: Val::Percent(100.), + height: Val::Percent(100.), + ..default() + }, + // Needs to be set to prevent a bug where nothing is displayed + background_color: Color::WHITE.into(), ..default() }, - // Needs to be set to prevent a bug where nothing is displayed - background_color: Color::WHITE.into(), - ..default() - }, - ); - - let cosmic_edit = commands.spawn(cosmic_edit).id(); + ) + // point editor at this entity. + // Plugin looks for UiImage and sets it's + // texture to the editor's rendered image + .insert(CosmicSource(cosmic_edit)); commands.insert_resource(Focus(Some(cosmic_edit))); } diff --git a/examples/bevy_api_testing.rs b/examples/bevy_api_testing.rs index 892ccb08c48dc9833d7e089c5232b2a77c6456e8..742f5f2e2b302286b7bc796c68a565e8e7fa514e 100644 --- a/examples/bevy_api_testing.rs +++ b/examples/bevy_api_testing.rs @@ -4,7 +4,7 @@ use bevy_cosmic_edit::*; fn setup(mut commands: Commands) { commands.spawn(Camera2dBundle::default()); - // spawn a new CosmicEditBundle + // UI editor let ui_editor = commands .spawn(CosmicEditBundle { attrs: CosmicAttrs(AttrsOwned::new( @@ -13,30 +13,33 @@ fn setup(mut commands: Commands) { max_lines: CosmicMaxLines(1), ..default() }) - .insert(ButtonBundle { + .insert(CosmicEditPlaceholderBundle { + text_setter: PlaceholderText(CosmicText::OneStyle("Placeholder".into())), + attrs: PlaceholderAttrs(AttrsOwned::new( + Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), + )), + }) + .id(); + + commands + .spawn(ButtonBundle { style: Style { // Size and position of text box width: Val::Px(300.), height: Val::Px(50.), left: Val::Px(100.), top: Val::Px(100.), - // needs to be set to prevent a bug where nothing is displayed ..default() }, + // needs to be set to prevent a bug where nothing is displayed background_color: BackgroundColor(Color::WHITE), ..default() }) - .insert(CosmicEditPlaceholderBundle { - text_setter: PlaceholderText(CosmicText::OneStyle("Placeholder".into())), - attrs: PlaceholderAttrs(AttrsOwned::new( - Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), - )), - }) - .id(); + .insert(CosmicSource(ui_editor)); - commands.spawn(( - CosmicEditBundle { ..default() }, - SpriteBundle { + // Sprite editor + commands.spawn((CosmicEditBundle { + sprite_bundle: SpriteBundle { // Sets size of text box sprite: Sprite { custom_size: Some(Vec2::new(300., 100.)), @@ -46,7 +49,8 @@ fn setup(mut commands: Commands) { transform: Transform::from_xyz(0., 100., 0.), ..default() }, - )); + ..default() + },)); commands.insert_resource(Focus(Some(ui_editor))); } @@ -63,16 +67,13 @@ fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor { fn change_active_editor_ui( mut commands: Commands, mut interaction_query: Query< - (&Interaction, Entity), - ( - Changed<Interaction>, - (With<CosmicEditor>, Without<ReadOnly>), - ), + (&Interaction, &CosmicSource), + (Changed<Interaction>, Without<ReadOnly>), >, ) { - for (interaction, entity) in interaction_query.iter_mut() { + for (interaction, source) in interaction_query.iter_mut() { if let Interaction::Pressed = interaction { - commands.insert_resource(Focus(Some(entity))); + commands.insert_resource(Focus(Some(source.0))); } } } @@ -82,7 +83,7 @@ fn change_active_editor_sprite( windows: Query<&Window, With<PrimaryWindow>>, buttons: Res<Input<MouseButton>>, mut cosmic_edit_query: Query< - (&mut Sprite, &GlobalTransform, Entity), + (&mut Sprite, &GlobalTransform, &Visibility, Entity), (With<CosmicEditor>, Without<ReadOnly>), >, camera_q: Query<(&Camera, &GlobalTransform)>, @@ -90,8 +91,11 @@ fn change_active_editor_sprite( let window = windows.single(); let (camera, camera_transform) = camera_q.single(); if buttons.just_pressed(MouseButton::Left) { - for (sprite, node_transform, entity) in &mut cosmic_edit_query.iter_mut() { - let size = sprite.custom_size.unwrap_or(Vec2::new(1., 1.)); + for (sprite, node_transform, visibility, entity) in &mut cosmic_edit_query.iter_mut() { + if visibility == Visibility::Hidden { + continue; + } + let size = sprite.custom_size.unwrap_or(Vec2::ONE); let x_min = node_transform.affine().translation.x - size.x / 2.; let y_min = node_transform.affine().translation.y - size.y / 2.; let x_max = node_transform.affine().translation.x + size.x / 2.; diff --git a/examples/every_option.rs b/examples/every_option.rs index 28dbbae701d38926fb70e5683938f9a979c997e7..048879f61ef544c8a9b69a49dc3faa64460948ac 100644 --- a/examples/every_option.rs +++ b/examples/every_option.rs @@ -26,9 +26,33 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { max_lines: CosmicMaxLines(1), text_setter: CosmicText::OneStyle("BANANA IS THE CODEWORD!".into()), mode: CosmicMode::Wrap, - canvas: Default::default(), + // CosmicEdit draws to this spritebundle + sprite_bundle: SpriteBundle { + sprite: Sprite { + // when using another target like a UI element, this is overridden + custom_size: Some(Vec2::ONE * 128.0), + ..default() + }, + // this is the default behaviour for targeting UI elements. + // If wanting a sprite, define your own SpriteBundle and + // leave the visibility on. See examples/basic_sprite.rs + visibility: Visibility::Hidden, + ..default() + }, + // Computed fields + padding: Default::default(), + widget_size: Default::default(), }) - .insert(ButtonBundle { + .insert(CosmicEditPlaceholderBundle { + text_setter: PlaceholderText(CosmicText::OneStyle("Placeholder".into())), + attrs: PlaceholderAttrs(AttrsOwned::new( + Attrs::new().color(CosmicColor::rgb(88, 88, 88)), + )), + }) + .id(); + + commands + .spawn(ButtonBundle { border_color: Color::LIME_GREEN.into(), style: Style { // Size and position of text box @@ -42,13 +66,7 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { background_color: Color::WHITE.into(), ..default() }) - .insert(CosmicEditPlaceholderBundle { - text_setter: PlaceholderText(CosmicText::OneStyle("Placeholder".into())), - attrs: PlaceholderAttrs(AttrsOwned::new( - Attrs::new().color(CosmicColor::rgb(88, 88, 88)), - )), - }) - .id(); + .insert(CosmicSource(editor)); commands.insert_resource(Focus(Some(editor))); diff --git a/examples/font_per_widget.rs b/examples/font_per_widget.rs index a5cc79f70a11134e9992e8a7144345d30cb52268..0f513566c21d93a42f3402c296ee895145f4d090 100644 --- a/examples/font_per_widget.rs +++ b/examples/font_per_widget.rs @@ -207,8 +207,8 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { )], ]; - let cosmic_edit_1 = ( - CosmicEditBundle { + let cosmic_edit_1 = commands + .spawn(CosmicEditBundle { text_position: bevy_cosmic_edit::CosmicTextPosition::Center, attrs: CosmicAttrs(AttrsOwned::new(attrs)), metrics: CosmicMetrics { @@ -218,24 +218,15 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { }, text_setter: CosmicText::MultiStyle(lines), ..default() - }, - ButtonBundle { - style: Style { - width: Val::Percent(50.), - height: Val::Percent(100.), - ..default() - }, - background_color: BackgroundColor(Color::WHITE), - ..default() - }, - ); + }) + .id(); let mut attrs_2 = Attrs::new(); attrs_2 = attrs_2.family(Family::Name("Times New Roman")); attrs_2.color_opt = Some(bevy_color_to_cosmic(Color::PURPLE)); - let cosmic_edit_2 = ( - CosmicEditBundle { + let cosmic_edit_2 = commands + .spawn(CosmicEditBundle { attrs: CosmicAttrs(AttrsOwned::new(attrs_2)), metrics: CosmicMetrics { font_size: 28., @@ -245,27 +236,38 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { text_position: CosmicTextPosition::Center, text_setter: CosmicText::OneStyle("Widget 2.\nClick on me =>".to_string()), ..default() - }, - ButtonBundle { - background_color: BackgroundColor(Color::WHITE.with_a(0.8)), - style: Style { - width: Val::Percent(50.), - height: Val::Percent(100.), - ..default() - }, - ..default() - }, - ); + }) + .id(); - let mut id = None; // Spawn the CosmicEditUiBundles as children of root commands.entity(root).with_children(|parent| { - id = Some(parent.spawn(cosmic_edit_1).id()); - parent.spawn(cosmic_edit_2); + parent + .spawn(ButtonBundle { + style: Style { + width: Val::Percent(50.), + height: Val::Percent(100.), + ..default() + }, + background_color: BackgroundColor(Color::WHITE), + ..default() + }) + .insert(CosmicSource(cosmic_edit_1)); + + parent + .spawn(ButtonBundle { + background_color: BackgroundColor(Color::WHITE.with_a(0.8)), + style: Style { + width: Val::Percent(50.), + height: Val::Percent(100.), + ..default() + }, + ..default() + }) + .insert(CosmicSource(cosmic_edit_2)); }); // Set active editor - commands.insert_resource(Focus(id)); + commands.insert_resource(Focus(Some(cosmic_edit_1))); } fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor { @@ -280,16 +282,13 @@ fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor { fn change_active_editor_ui( mut commands: Commands, mut interaction_query: Query< - (&Interaction, Entity), - ( - Changed<Interaction>, - (With<CosmicEditor>, Without<ReadOnly>), - ), + (&Interaction, &CosmicSource), + (Changed<Interaction>, Without<ReadOnly>), >, ) { - for (interaction, entity) in interaction_query.iter_mut() { + for (interaction, source) in interaction_query.iter_mut() { if let Interaction::Pressed = interaction { - commands.insert_resource(Focus(Some(entity))); + commands.insert_resource(Focus(Some(source.0))); } } } diff --git a/examples/image_background.rs b/examples/image_background.rs index 8d700ae94948aff58409939d025b369b1c34e6a1..1b9ed6db508a34a998dbf8a73b32ddcc2b99c18f 100644 --- a/examples/image_background.rs +++ b/examples/image_background.rs @@ -14,7 +14,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { background_image: CosmicBackground(Some(bg_image_handle)), ..default() }) - .insert(ButtonBundle { + .id(); + + commands + .spawn(ButtonBundle { style: Style { // Size and position of text box width: Val::Px(300.), @@ -26,7 +29,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { background_color: Color::WHITE.into(), ..default() }) - .id(); + .insert(CosmicSource(editor)); + commands.insert_resource(Focus(Some(editor))); } diff --git a/examples/login.rs b/examples/login.rs index 8670a50c70fde696b80a8aac8a45791b0f89fcda..1dbee608853aac9285c008fb9abb4856f8a031ae 100644 --- a/examples/login.rs +++ b/examples/login.rs @@ -21,6 +21,90 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) { commands.spawn(Camera2dBundle::default()); + let login_editor = commands + .spawn(CosmicEditBundle { + max_lines: CosmicMaxLines(1), + metrics: CosmicMetrics { + scale_factor: window.scale_factor() as f32, + ..default() + }, + sprite_bundle: SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(300.0, 50.0)), + ..default() + }, + visibility: Visibility::Hidden, + ..default() + }, + ..default() + }) + .insert(CosmicEditPlaceholderBundle { + text_setter: PlaceholderText(CosmicText::OneStyle("Username".into())), + attrs: PlaceholderAttrs(AttrsOwned::new( + Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), + )), + }) + .insert(UsernameTag) + .id(); + + let password_editor = commands + .spawn(CosmicEditBundle { + max_lines: CosmicMaxLines(1), + metrics: CosmicMetrics { + scale_factor: window.scale_factor() as f32, + ..default() + }, + ..default() + }) + .insert(CosmicEditPlaceholderBundle { + text_setter: PlaceholderText(CosmicText::OneStyle("Password".into())), + attrs: PlaceholderAttrs(AttrsOwned::new( + Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), + )), + }) + .insert(PasswordTag) + .insert(PasswordInput::default()) + .id(); + + let submit_editor = commands + .spawn(CosmicEditBundle { + max_lines: CosmicMaxLines(1), + metrics: CosmicMetrics { + font_size: 25.0, + line_height: 25.0, + scale_factor: window.scale_factor() as f32, + ..default() + }, + attrs: CosmicAttrs(AttrsOwned::new( + Attrs::new().color(bevy_color_to_cosmic(Color::WHITE)), + )), + text_setter: CosmicText::OneStyle("Submit".into()), + fill_color: FillColor(Color::GREEN), + ..default() + }) + .insert(ReadOnly) + .id(); + + let display_editor = commands + .spawn(CosmicEditBundle { + metrics: CosmicMetrics { + scale_factor: window.scale_factor() as f32, + ..default() + }, + ..default() + }) + .insert(CosmicEditPlaceholderBundle { + text_setter: PlaceholderText(CosmicText::OneStyle("Output".into())), + attrs: PlaceholderAttrs(AttrsOwned::new( + Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), + )), + }) + .insert((ReadOnly, DisplayTag)) + .id(); + + commands.insert_resource(Focus(Some(login_editor))); + + // Spawn UI commands .spawn(NodeBundle { style: Style { @@ -34,15 +118,7 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) { ..default() }) .with_children(|root| { - root.spawn(CosmicEditBundle { - max_lines: CosmicMaxLines(1), - metrics: CosmicMetrics { - scale_factor: window.scale_factor() as f32, - ..default() - }, - ..default() - }) - .insert(ButtonBundle { + root.spawn(ButtonBundle { style: Style { // Size and position of text box width: Val::Px(300.), @@ -53,23 +129,9 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) { background_color: BackgroundColor(Color::WHITE), ..default() }) - .insert(CosmicEditPlaceholderBundle { - text_setter: PlaceholderText(CosmicText::OneStyle("Username".into())), - attrs: PlaceholderAttrs(AttrsOwned::new( - Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), - )), - }) - .insert(UsernameTag); + .insert(CosmicSource(login_editor)); - root.spawn(CosmicEditBundle { - max_lines: CosmicMaxLines(1), - metrics: CosmicMetrics { - scale_factor: window.scale_factor() as f32, - ..default() - }, - ..default() - }) - .insert(ButtonBundle { + root.spawn(ButtonBundle { style: Style { // Size and position of text box width: Val::Px(300.), @@ -80,31 +142,9 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) { background_color: BackgroundColor(Color::WHITE), ..default() }) - .insert(CosmicEditPlaceholderBundle { - text_setter: PlaceholderText(CosmicText::OneStyle("Password".into())), - attrs: PlaceholderAttrs(AttrsOwned::new( - Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), - )), - }) - .insert(PasswordTag) - .insert(PasswordInput::default()); - - root.spawn(CosmicEditBundle { - max_lines: CosmicMaxLines(1), - metrics: CosmicMetrics { - font_size: 25.0, - line_height: 25.0, - scale_factor: window.scale_factor() as f32, - ..default() - }, - attrs: CosmicAttrs(AttrsOwned::new( - Attrs::new().color(bevy_color_to_cosmic(Color::WHITE)), - )), - text_setter: CosmicText::OneStyle("Submit".into()), - fill_color: FillColor(Color::GREEN), - ..default() - }) - .insert(ButtonBundle { + .insert(CosmicSource(password_editor)); + + root.spawn(ButtonBundle { style: Style { // Size and position of text box width: Val::Px(150.), @@ -119,16 +159,9 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) { ..default() }) .insert(SubmitButton) - .insert(ReadOnly); + .insert(CosmicSource(submit_editor)); - root.spawn(CosmicEditBundle { - metrics: CosmicMetrics { - scale_factor: window.scale_factor() as f32, - ..default() - }, - ..default() - }) - .insert(ButtonBundle { + root.spawn(ButtonBundle { style: Style { // Size and position of text box width: Val::Px(300.), @@ -139,13 +172,7 @@ fn setup(mut commands: Commands, window: Query<&Window, With<PrimaryWindow>>) { background_color: BackgroundColor(Color::WHITE), ..default() }) - .insert(CosmicEditPlaceholderBundle { - text_setter: PlaceholderText(CosmicText::OneStyle("Output".into())), - attrs: PlaceholderAttrs(AttrsOwned::new( - Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))), - )), - }) - .insert((ReadOnly, DisplayTag)); + .insert(CosmicSource(display_editor)); }); } @@ -161,16 +188,13 @@ fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor { fn change_active_editor_ui( mut commands: Commands, mut interaction_query: Query< - (&Interaction, Entity), - ( - Changed<Interaction>, - (With<CosmicEditor>, Without<ReadOnly>), - ), + (&Interaction, &CosmicSource), + (Changed<Interaction>, Without<ReadOnly>), >, ) { - for (interaction, entity) in interaction_query.iter_mut() { + for (interaction, source) in interaction_query.iter_mut() { if let Interaction::Pressed = interaction { - commands.insert_resource(Focus(Some(entity))); + commands.insert_resource(Focus(Some(source.0))); } } } diff --git a/examples/multiple_sprites.rs b/examples/multiple_sprites.rs index dc2f06fce6b10f7554659058ba95541eb3957824..0366731bb53f409ab1731d92adaa02fea4678476 100644 --- a/examples/multiple_sprites.rs +++ b/examples/multiple_sprites.rs @@ -20,16 +20,13 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { scale_factor: primary_window.scale_factor() as f32, }; - let cosmic_edit_1 = ( - CosmicEditBundle { - attrs: CosmicAttrs(AttrsOwned::new(attrs)), - metrics: metrics.clone(), - text_position: CosmicTextPosition::Center, - fill_color: FillColor(Color::ALICE_BLUE), - text_setter: CosmicText::OneStyle("馃榾馃榾馃榾 x => y".to_string()), - ..default() - }, - SpriteBundle { + let cosmic_edit_1 = (CosmicEditBundle { + attrs: CosmicAttrs(AttrsOwned::new(attrs)), + metrics: metrics.clone(), + text_position: CosmicTextPosition::Center, + fill_color: FillColor(Color::ALICE_BLUE), + text_setter: CosmicText::OneStyle("馃榾馃榾馃榾 x => y".to_string()), + sprite_bundle: SpriteBundle { sprite: Sprite { custom_size: Some(Vec2 { x: primary_window.width() / 2., @@ -40,18 +37,16 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { transform: Transform::from_translation(Vec3::new(-primary_window.width() / 4., 0., 1.)), ..default() }, - ); + ..default() + },); - let cosmic_edit_2 = ( - CosmicEditBundle { - attrs: CosmicAttrs(AttrsOwned::new(attrs)), - metrics, - text_position: CosmicTextPosition::Center, - fill_color: FillColor(Color::GRAY.with_a(0.5)), - text_setter: CosmicText::OneStyle("Widget_2. Click on me".to_string()), - ..default() - }, - SpriteBundle { + let cosmic_edit_2 = (CosmicEditBundle { + attrs: CosmicAttrs(AttrsOwned::new(attrs)), + metrics, + text_position: CosmicTextPosition::Center, + fill_color: FillColor(Color::GRAY.with_a(0.5)), + text_setter: CosmicText::OneStyle("Widget_2. Click on me".to_string()), + sprite_bundle: SpriteBundle { sprite: Sprite { custom_size: Some(Vec2 { x: primary_window.width() / 2., @@ -66,7 +61,8 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { )), ..default() }, - ); + ..default() + },); let id = commands.spawn(cosmic_edit_1).id(); diff --git a/examples/readonly.rs b/examples/readonly.rs index e218b099058122cb8bf36eb563f9532d55fe0a27..0d7aa8c053fa7aa40d2d66ab526539dd0a34d8a6 100644 --- a/examples/readonly.rs +++ b/examples/readonly.rs @@ -20,8 +20,9 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { attrs = attrs.family(Family::Name("Victor Mono")); attrs = attrs.color(bevy_color_to_cosmic(Color::PURPLE)); - let cosmic_edit = ( - CosmicEditBundle { + // spawn editor + let cosmic_edit = commands + .spawn(CosmicEditBundle { attrs: CosmicAttrs(AttrsOwned::new(attrs)), text_position: CosmicTextPosition::Center, metrics: CosmicMetrics { @@ -31,25 +32,27 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { }, text_setter: CosmicText::OneStyle("馃榾馃榾馃榾 x => y\nRead only widget".to_string()), ..default() - }, - ButtonBundle { - style: Style { - width: Val::Percent(100.), - height: Val::Percent(100.), - ..default() - }, - background_color: BackgroundColor(Color::WHITE), - ..default() - }, - ); + }) + .insert(ReadOnly) + .id(); - let mut id = None; - // Spawn the CosmicEditUiBundle as a child of root + // Spawn the ButtonBundle as a child of root commands.entity(root).with_children(|parent| { - id = Some(parent.spawn(cosmic_edit).insert(ReadOnly).id()); + parent + .spawn(ButtonBundle { + style: Style { + width: Val::Percent(100.), + height: Val::Percent(100.), + ..default() + }, + background_color: BackgroundColor(Color::WHITE), + ..default() + }) + // add cosmic source + .insert(CosmicSource(cosmic_edit)); }); - commands.insert_resource(Focus(id)); + commands.insert_resource(Focus(Some(cosmic_edit))); } pub fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor { diff --git a/examples/text_input.rs b/examples/text_input.rs index 7ba06f178d7668a5c9f19cc742d9724296306973..f65e4dad0e2fc4150cf09dad2a366872629dd03c 100644 --- a/examples/text_input.rs +++ b/examples/text_input.rs @@ -9,7 +9,7 @@ fn create_editable_widget(commands: &mut Commands, scale_factor: f32, text: Stri AttrsOwned::new(Attrs::new().color(bevy_color_to_cosmic(Color::hex("4d4d4d").unwrap()))); let placeholder_attrs = AttrsOwned::new(Attrs::new().color(bevy_color_to_cosmic(Color::hex("#e6e6e6").unwrap()))); - commands + let editor = commands .spawn(( CosmicEditBundle { attrs: CosmicAttrs(attrs.clone()), @@ -24,31 +24,36 @@ fn create_editable_widget(commands: &mut Commands, scale_factor: f32, text: Stri mode: CosmicMode::InfiniteLine, ..default() }, - ButtonBundle { - border_color: Color::hex("#ededed").unwrap().into(), - style: Style { - border: UiRect::all(Val::Px(3.)), - width: Val::Percent(20.), - height: Val::Px(50.), - left: Val::Percent(40.), - top: Val::Px(100.), - ..default() - }, - background_color: Color::WHITE.into(), - ..default() - }, CosmicEditPlaceholderBundle { text_setter: PlaceholderText(CosmicText::OneStyle("Type something...".into())), attrs: PlaceholderAttrs(placeholder_attrs.clone()), }, )) - .id() + .id(); + commands + .spawn(ButtonBundle { + border_color: Color::hex("#ededed").unwrap().into(), + style: Style { + border: UiRect::all(Val::Px(3.)), + width: Val::Percent(20.), + height: Val::Px(50.), + left: Val::Percent(40.), + top: Val::Px(100.), + ..default() + }, + background_color: Color::WHITE.into(), + ..default() + }) + .insert(CosmicSource(editor)); + + editor } fn create_readonly_widget(commands: &mut Commands, scale_factor: f32, text: String) -> Entity { let attrs = AttrsOwned::new(Attrs::new().color(bevy_color_to_cosmic(Color::hex("4d4d4d").unwrap()))); - commands + + let editor = commands .spawn(( CosmicEditBundle { attrs: CosmicAttrs(attrs.clone()), @@ -62,22 +67,27 @@ fn create_readonly_widget(commands: &mut Commands, scale_factor: f32, text: Stri mode: CosmicMode::AutoHeight, ..default() }, - ButtonBundle { - border_color: Color::hex("#ededed").unwrap().into(), - style: Style { - border: UiRect::all(Val::Px(3.)), - width: Val::Percent(20.), - height: Val::Px(50.), - left: Val::Percent(40.), - top: Val::Px(100.), - ..default() - }, - background_color: Color::WHITE.into(), - ..default() - }, ReadOnly, )) - .id() + .id(); + + commands + .spawn(ButtonBundle { + border_color: Color::hex("#ededed").unwrap().into(), + style: Style { + border: UiRect::all(Val::Px(3.)), + width: Val::Percent(20.), + height: Val::Px(50.), + left: Val::Percent(40.), + top: Val::Px(100.), + ..default() + }, + background_color: Color::WHITE.into(), + ..default() + }) + .insert(CosmicSource(editor)); + + editor } fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { @@ -94,12 +104,20 @@ fn setup(mut commands: Commands, windows: Query<&Window, With<PrimaryWindow>>) { fn handle_enter( mut commands: Commands, keys: Res<Input<KeyCode>>, - mut mode: Query<(Entity, &CosmicEditor, &mut CosmicMode)>, + mut query_dest: Query<(Entity, &CosmicSource)>, + mut query_source: Query<(Entity, &CosmicEditor, &CosmicMode)>, windows: Query<&Window, With<PrimaryWindow>>, ) { if keys.just_pressed(KeyCode::Return) { let scale_factor = windows.single().scale_factor() as f32; - for (entity, editor, mode) in mode.iter_mut() { + for (entity, editor, mode) in query_source.iter_mut() { + // Remove UI elements + for (dest_entity, source) in query_dest.iter_mut() { + if source.0 == entity { + commands.entity(dest_entity).despawn_recursive(); + } + } + let text = editor.get_text(); commands.entity(entity).despawn_recursive(); if *mode == CosmicMode::AutoHeight { diff --git a/src/cursor.rs b/src/cursor.rs index e0485b48e3c43edb3f805a4e00837c17044fbf5f..7c09799a0d3a17e4028d78830c5dfa082e5b0b85 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,6 +1,6 @@ use bevy::{input::mouse::MouseMotion, prelude::*, window::PrimaryWindow}; -use crate::{CosmicEditor, CosmicTextChanged}; +use crate::{CosmicEditor, CosmicSource, CosmicTextChanged}; #[cfg(feature = "multicam")] use crate::CosmicPrimaryCamera; @@ -45,7 +45,7 @@ type CameraQuery<'a, 'b, 'c, 'd> = Query<'a, 'b, (&'c Camera, &'d GlobalTransfor pub fn hover_sprites( windows: Query<&Window, With<PrimaryWindow>>, - mut cosmic_edit_query: Query<(&mut Sprite, &GlobalTransform), With<CosmicEditor>>, + mut cosmic_edit_query: Query<(&mut Sprite, &Visibility, &GlobalTransform), With<CosmicEditor>>, camera_q: CameraQuery, mut hovered: Local<bool>, mut last_hovered: Local<bool>, @@ -55,8 +55,11 @@ pub fn hover_sprites( *hovered = false; let window = windows.single(); let (camera, camera_transform) = camera_q.single(); - for (sprite, node_transform) in &mut cosmic_edit_query.iter_mut() { - let size = sprite.custom_size.unwrap_or(Vec2::new(1., 1.)); + for (sprite, visibility, node_transform) in &mut cosmic_edit_query.iter_mut() { + if visibility == Visibility::Hidden { + continue; + } + let size = sprite.custom_size.unwrap_or(Vec2::ONE); let x_min = node_transform.affine().translation.x - size.x / 2.; let y_min = node_transform.affine().translation.y - size.y / 2.; let x_max = node_transform.affine().translation.x + size.x / 2.; @@ -82,7 +85,7 @@ pub fn hover_sprites( } pub fn hover_ui( - mut interaction_query: Query<&Interaction, (Changed<Interaction>, With<CosmicEditor>)>, + mut interaction_query: Query<&Interaction, (Changed<Interaction>, With<CosmicSource>)>, mut evw_hover_in: EventWriter<TextHoverIn>, mut evw_hover_out: EventWriter<TextHoverOut>, ) { diff --git a/src/input.rs b/src/input.rs index 3ef16bab07b7ba42ca44e038644033725edead36..b5486f4223f9b3d8c7132f5d6f6df093279470e1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -22,8 +22,8 @@ use wasm_bindgen_futures::JsFuture; use crate::{ get_node_cursor_pos, get_timestamp, get_x_offset_center, get_y_offset_center, save_edit_history, CosmicAttrs, CosmicEditHistory, CosmicEditor, CosmicFontSystem, - CosmicMaxChars, CosmicMaxLines, CosmicTextChanged, CosmicTextPosition, Focus, PasswordInput, - ReadOnly, XOffset, + CosmicMaxChars, CosmicMaxLines, CosmicSource, CosmicTextChanged, CosmicTextPosition, Focus, + PasswordInput, ReadOnly, XOffset, }; #[derive(Resource)] @@ -47,15 +47,15 @@ pub(crate) fn input_mouse( active_editor: Res<Focus>, keys: Res<Input<KeyCode>>, buttons: Res<Input<MouseButton>>, - mut cosmic_edit_query: Query<( + mut editor_q: Query<( &mut CosmicEditor, &GlobalTransform, &CosmicTextPosition, Entity, &XOffset, - Option<&mut Node>, - Option<&mut Sprite>, + &mut Sprite, )>, + node_q: Query<(&Node, &GlobalTransform, &CosmicSource)>, mut font_system: ResMut<CosmicFontSystem>, mut scroll_evr: EventReader<MouseWheel>, camera_q: Query<(&Camera, &GlobalTransform)>, @@ -86,21 +86,29 @@ pub(crate) fn input_mouse( let primary_window = windows.single(); let scale_factor = primary_window.scale_factor() as f32; let (camera, camera_transform) = camera_q.iter().find(|(c, _)| c.is_active).unwrap(); - for (mut editor, node_transform, text_position, entity, x_offset, node_opt, sprite_opt) in - &mut cosmic_edit_query.iter_mut() + + for (mut editor, sprite_transform, text_position, entity, x_offset, sprite) in + &mut editor_q.iter_mut() { if active_editor.0 != Some(entity) { continue; } - let (width, height, is_ui_node) = match node_opt { - Some(node) => (node.size().x, node.size().y, true), - None => { - let sprite = sprite_opt.unwrap(); - let size = sprite.custom_size.unwrap(); - (size.x, size.y, false) + let mut is_ui_node = false; + let mut transform = sprite_transform; + let (mut width, mut height) = + (sprite.custom_size.unwrap().x, sprite.custom_size.unwrap().y); + + // TODO: this is bad loop nesting, rethink system with relationships in mind + for (node, node_transform, source) in node_q.iter() { + if source.0 != entity { + continue; } - }; + is_ui_node = true; + transform = node_transform; + width = node.size().x; + height = node.size().y; + } let shift = keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]); @@ -132,7 +140,7 @@ pub(crate) fn input_mouse( if buttons.just_pressed(MouseButton::Left) { if let Some(node_cursor_pos) = get_node_cursor_pos( primary_window, - node_transform, + transform, (width, height), is_ui_node, camera, @@ -171,7 +179,7 @@ pub(crate) fn input_mouse( if buttons.pressed(MouseButton::Left) && *click_count == 0 { if let Some(node_cursor_pos) = get_node_cursor_pos( primary_window, - node_transform, + transform, (width, height), is_ui_node, camera, @@ -213,8 +221,6 @@ pub(crate) fn input_mouse( } // TODO: split copy/paste into own fn, separate fn for wasm -// Maybe split undo/redo too, just drop inputs from this fn when pressed -/// Handles undo/redo, copy/paste and char input pub(crate) fn input_kb( active_editor: Res<Focus>, keys: Res<Input<KeyCode>>, diff --git a/src/lib.rs b/src/lib.rs index 59e8013d9d9ead303415122a883fba44220e15bf..0b3d479fe29dd539c443d1ef96c717e1e1837fe1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ mod render; use std::{collections::VecDeque, path::PathBuf}; -use bevy::{prelude::*, render::texture::DEFAULT_IMAGE_HANDLE, transform::TransformSystem}; +use bevy::{prelude::*, transform::TransformSystem}; pub use cosmic_text::{ Action, Attrs, AttrsOwned, Color as CosmicColor, Cursor, Edit, Family, Style as FontStyle, Weight as FontWeight, @@ -20,10 +20,10 @@ use input::{input_kb, input_mouse, undo_redo, ClickTimer}; #[cfg(target_arch = "wasm32")] use input::{poll_wasm_paste, WasmPaste, WasmPasteAsyncChannel}; use render::{ - blink_cursor, cosmic_edit_redraw_buffer, freeze_cursor_blink, hide_inactive_or_readonly_cursor, - hide_password_text, on_scale_factor_change, restore_password_text, restore_placeholder_text, - set_initial_scale, show_placeholder, CursorBlinkTimer, CursorVisibility, PasswordValues, - SwashCacheState, + blink_cursor, freeze_cursor_blink, hide_inactive_or_readonly_cursor, hide_password_text, + on_scale_factor_change, restore_password_text, restore_placeholder_text, set_initial_scale, + show_placeholder, CosmicPadding, CosmicRenderSet, CosmicWidgetSize, CursorBlinkTimer, + CursorVisibility, PasswordValues, SwashCacheState, }; #[cfg(feature = "multicam")] @@ -221,15 +221,9 @@ impl Default for PasswordInput { } #[derive(Component)] -pub struct CosmicCanvas(pub Handle<Image>); +pub struct CosmicSource(pub Entity); -impl Default for CosmicCanvas { - fn default() -> Self { - CosmicCanvas(DEFAULT_IMAGE_HANDLE.typed()) - } -} - -#[derive(Bundle, Default)] +#[derive(Bundle)] pub struct CosmicEditBundle { // cosmic bits pub fill_color: FillColor, @@ -241,7 +235,36 @@ pub struct CosmicEditBundle { pub max_chars: CosmicMaxChars, pub text_setter: CosmicText, pub mode: CosmicMode, - pub canvas: CosmicCanvas, + pub sprite_bundle: SpriteBundle, + // render bits + pub padding: CosmicPadding, + pub widget_size: CosmicWidgetSize, +} + +impl Default for CosmicEditBundle { + fn default() -> Self { + CosmicEditBundle { + fill_color: Default::default(), + text_position: Default::default(), + metrics: Default::default(), + attrs: Default::default(), + background_image: Default::default(), + max_lines: Default::default(), + max_chars: Default::default(), + text_setter: Default::default(), + mode: Default::default(), + sprite_bundle: SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::ONE * 128.0), + ..default() + }, + visibility: Visibility::Hidden, + ..default() + }, + padding: Default::default(), + widget_size: Default::default(), + } + } } #[derive(Bundle)] @@ -305,8 +328,20 @@ impl Plugin for CosmicEditPlugin { freeze_cursor_blink, hide_inactive_or_readonly_cursor, clear_inactive_selection, - render::update_handle_ui, - render::update_handle_sprite, + ); + + let render_systems = ( + render::new_image_from_default.in_set(CosmicRenderSet::Setup), + render::set_size_from_ui.in_set(CosmicRenderSet::Setup), + render::cosmic_reshape.in_set(CosmicRenderSet::Shaping), + render::cosmic_widget_size.in_set(CosmicRenderSet::Sizing), + render::cosmic_buffer_size.in_set(CosmicRenderSet::Sizing), + render::auto_height + .after(CosmicRenderSet::Sizing) + .before(CosmicRenderSet::Draw), + render::cosmic_padding.in_set(CosmicRenderSet::Padding), + render::set_cursor.in_set(CosmicRenderSet::Cursor), + render::render_texture.in_set(CosmicRenderSet::Draw), ); app.add_systems( @@ -314,8 +349,7 @@ impl Plugin for CosmicEditPlugin { ( set_initial_scale, (cosmic_editor_builder, on_scale_factor_change).after(set_initial_scale), - render::cosmic_ui_to_canvas, - render::cosmic_sprite_to_canvas, + render::swap_target_handle, ), ) .add_systems( @@ -329,12 +363,25 @@ impl Plugin for CosmicEditPlugin { ) .chain(), ) + .configure_sets( + PostUpdate, + ( + CosmicRenderSet::Setup, + CosmicRenderSet::Shaping, + CosmicRenderSet::Sizing, + CosmicRenderSet::Cursor, + CosmicRenderSet::Padding, + CosmicRenderSet::Draw, + ) + .chain() + .after(TransformSystem::TransformPropagate), + ) .add_systems( PostUpdate, ( hide_password_text, show_placeholder, - cosmic_edit_redraw_buffer.after(TransformSystem::TransformPropagate), + render_systems, apply_deferred, // Prevents one-frame inputs adding placeholder to editor restore_password_text, restore_placeholder_text, diff --git a/src/render.rs b/src/render.rs index baffb0f8e6b10a920d7524cc981cf4abc0e6f258..39921911a73662289fe16a05b105ce80d7ec869b 100644 --- a/src/render.rs +++ b/src/render.rs @@ -3,7 +3,7 @@ use std::time::Duration; use bevy::{ asset::HandleId, prelude::*, - render::render_resource::Extent3d, + render::{render_resource::Extent3d, texture::DEFAULT_IMAGE_HANDLE}, utils::HashMap, window::{PrimaryWindow, WindowScaleFactorChanged}, }; @@ -12,11 +12,21 @@ use image::{imageops::FilterType, GenericImageView}; use crate::{ get_text_size, get_x_offset_center, get_y_offset_center, CosmicAttrs, CosmicBackground, - CosmicCanvas, CosmicEditor, CosmicFontSystem, CosmicMetrics, CosmicMode, CosmicText, + CosmicEditor, CosmicFontSystem, CosmicMetrics, CosmicMode, CosmicSource, CosmicText, CosmicTextPosition, FillColor, Focus, PasswordInput, PlaceholderAttrs, PlaceholderText, ReadOnly, XOffset, DEFAULT_SCALE_PLACEHOLDER, }; +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +pub enum CosmicRenderSet { + Setup, + Shaping, + Sizing, + Cursor, + Padding, + Draw, +} + #[derive(Resource)] pub(crate) struct SwashCacheState { pub swash_cache: SwashCache, @@ -34,137 +44,115 @@ pub(crate) struct PasswordValues(pub HashMap<Entity, (String, usize)>); #[derive(Component)] pub(crate) struct Placeholder; -pub(crate) fn cosmic_edit_redraw_buffer( - windows: Query<&Window, With<PrimaryWindow>>, - mut images: ResMut<Assets<Image>>, - mut swash_cache_state: ResMut<SwashCacheState>, - mut cosmic_edit_query: Query<( - &mut CosmicEditor, - &CosmicAttrs, - &CosmicBackground, - &FillColor, - &mut CosmicCanvas, +#[derive(Component, Default)] +pub struct CosmicPadding(pub Vec2); + +#[derive(Component, Default)] +pub struct CosmicWidgetSize(pub Vec2); + +pub(crate) fn cosmic_padding( + mut query: Query<( + &mut CosmicPadding, &CosmicTextPosition, - Option<&Node>, - Option<&mut Style>, - Option<&mut Sprite>, - &mut XOffset, - &CosmicMode, + &CosmicEditor, + &CosmicWidgetSize, )>, - mut font_system: ResMut<CosmicFontSystem>, ) { - let primary_window = windows.single(); - let scale = primary_window.scale_factor() as f32; - - for ( - mut cosmic_editor, - attrs, - background_image, - fill_color, - mut canvas, - text_position, - node_opt, - style_opt, - sprite_opt, - mut x_offset, - mode, - ) in &mut cosmic_edit_query.iter_mut() - { - if !cosmic_editor.0.buffer().redraw() { - continue; - } - - let editor = &mut cosmic_editor.0; - - editor.shape_as_needed(&mut font_system.0); - - // Get numbers, do maths to find and set cursor - // - let (base_width, mut base_height) = match node_opt { - Some(node) => (node.size().x.ceil(), node.size().y.ceil()), - None => ( - sprite_opt.as_ref().unwrap().custom_size.unwrap().x.ceil(), - sprite_opt.as_ref().unwrap().custom_size.unwrap().y.ceil(), + for (mut padding, position, editor, size) in query.iter_mut() { + padding.0 = match position { + CosmicTextPosition::Center => Vec2::new( + get_x_offset_center(size.0.x, editor.0.buffer()) as f32, + get_y_offset_center(size.0.y, editor.0.buffer()) as f32, ), - }; + CosmicTextPosition::TopLeft { padding } => Vec2::new(*padding as f32, *padding as f32), + CosmicTextPosition::Left { padding } => Vec2::new( + *padding as f32, + get_y_offset_center(size.0.y, editor.0.buffer()) as f32, + ), + } + } +} - let widget_width = base_width * scale; - let widget_height = base_height * scale; +pub(crate) fn cosmic_widget_size( + mut query: Query<(&mut CosmicWidgetSize, &Sprite), Changed<Sprite>>, + windows: Query<&Window, With<PrimaryWindow>>, +) { + let scale = windows.single().scale_factor() as f32; + for (mut size, sprite) in query.iter_mut() { + size.0 = sprite.custom_size.unwrap().ceil() * scale; + } +} - let padding_x = match text_position { +pub(crate) fn cosmic_buffer_size( + mut query: Query<( + &mut CosmicEditor, + &CosmicMode, + &CosmicWidgetSize, + &CosmicTextPosition, + )>, + mut font_system: ResMut<CosmicFontSystem>, +) { + for (mut editor, mode, size, position) in query.iter_mut() { + let padding_x = match position { CosmicTextPosition::Center => 0., CosmicTextPosition::TopLeft { padding } => *padding as f32, CosmicTextPosition::Left { padding } => *padding as f32, }; let (buffer_width, buffer_height) = match mode { - CosmicMode::InfiniteLine => (f32::MAX, widget_height), - CosmicMode::AutoHeight => (widget_width - padding_x, (i32::MAX / 2) as f32), - CosmicMode::Wrap => (widget_width - padding_x, widget_height), + CosmicMode::InfiniteLine => (f32::MAX, size.0.y), + CosmicMode::AutoHeight => (size.0.x - padding_x, (i32::MAX / 2) as f32), + CosmicMode::Wrap => (size.0.x - padding_x, size.0.y), }; editor + .0 .buffer_mut() .set_size(&mut font_system.0, buffer_width, buffer_height); + } +} - if mode == &CosmicMode::AutoHeight { - let text_size = get_text_size(editor.buffer()); - let text_height = (text_size.1 + 30.) / primary_window.scale_factor() as f32; - if text_height > base_height { - base_height = text_height.ceil(); - match style_opt { - Some(mut style) => style.height = Val::Px(base_height), - None => sprite_opt.unwrap().custom_size.unwrap().y = base_height, - } - } - } - - let mut cursor_x = 0.; - if mode == &CosmicMode::InfiniteLine { - if let Some(line) = editor.buffer().layout_runs().next() { - for (idx, glyph) in line.glyphs.iter().enumerate() { - if editor.cursor().affinity == Affinity::Before { - if idx <= editor.cursor().index { - cursor_x += glyph.w; - } - } else if idx < editor.cursor().index { - cursor_x += glyph.w; - } else { - break; - } - } - } - } - - if mode == &CosmicMode::InfiniteLine && x_offset.0.is_none() { - let padding_x = match text_position { - CosmicTextPosition::Center => get_x_offset_center(widget_width, editor.buffer()), - CosmicTextPosition::TopLeft { padding } => *padding, - CosmicTextPosition::Left { padding } => *padding, - }; - *x_offset = XOffset(Some((0., widget_width - 2. * padding_x as f32))); - } +pub(crate) fn cosmic_reshape( + mut query: Query<&mut CosmicEditor>, + mut font_system: ResMut<CosmicFontSystem>, +) { + for mut cosmic_editor in query.iter_mut() { + cosmic_editor.0.shape_as_needed(&mut font_system.0); + } +} - if let Some((x_min, x_max)) = x_offset.0 { - if cursor_x > x_max { - let diff = cursor_x - x_max; - *x_offset = XOffset(Some((x_min + diff, cursor_x))); - } - if cursor_x < x_min { - let diff = x_min - cursor_x; - *x_offset = XOffset(Some((cursor_x, x_max - diff))); - } +pub(crate) fn render_texture( + mut query: Query<( + &mut CosmicEditor, + &CosmicAttrs, + &CosmicBackground, + &FillColor, + &Handle<Image>, + &CosmicWidgetSize, + &CosmicPadding, + &XOffset, + )>, + mut font_system: ResMut<CosmicFontSystem>, + mut images: ResMut<Assets<Image>>, + mut swash_cache_state: ResMut<SwashCacheState>, +) { + for (mut cosmic_editor, attrs, background_image, fill_color, canvas, size, padding, x_offset) in + query.iter_mut() + { + // TODO: redraw tag component + if !cosmic_editor.0.buffer().redraw() { + continue; } // Draw background - let mut pixels = vec![0; widget_width as usize * widget_height as usize * 4]; + let mut pixels = vec![0; size.0.x as usize * size.0.y as usize * 4]; if let Some(bg_image) = background_image.0.clone() { if let Some(image) = images.get(&bg_image) { let mut dynamic_image = image.clone().try_into_dynamic().unwrap(); - if image.size().x != widget_width || image.size().y != widget_height { + if image.size().x != size.0.x || image.size().y != size.0.y { dynamic_image = dynamic_image.resize_to_fill( - widget_width as u32, - widget_height as u32, + size.0.x as u32, + size.0.y as u32, FilterType::Triangle, ); } @@ -187,26 +175,13 @@ pub(crate) fn cosmic_edit_redraw_buffer( } } - // Get values for glyph draw step - let (padding_x, padding_y) = match text_position { - CosmicTextPosition::Center => ( - get_x_offset_center(widget_width, editor.buffer()), - get_y_offset_center(widget_height, editor.buffer()), - ), - CosmicTextPosition::TopLeft { padding } => (*padding, *padding), - CosmicTextPosition::Left { padding } => ( - *padding, - get_y_offset_center(widget_height, editor.buffer()), - ), - }; - let font_color = attrs .0 .color_opt .unwrap_or(cosmic_text::Color::rgb(0, 0, 0)); // Draw glyphs - editor.draw( + cosmic_editor.0.draw( &mut font_system.0, &mut swash_cache_state.swash_cache, font_color, @@ -215,10 +190,10 @@ pub(crate) fn cosmic_edit_redraw_buffer( for col in 0..w as i32 { draw_pixel( &mut pixels, - widget_width as i32, - widget_height as i32, - x + col + padding_x - x_offset.0.unwrap_or((0., 0.)).0 as i32, - y + row + padding_y, + size.0.x as i32, + size.0.y as i32, + x + col + padding.0.x as i32 - x_offset.0.unwrap_or((0., 0.)).0 as i32, + y + row + padding.0.y as i32, color, ); } @@ -226,37 +201,134 @@ pub(crate) fn cosmic_edit_redraw_buffer( }, ); - let canvas = &mut canvas.0; - if let Some(prev_image) = images.get_mut(canvas) { - if *canvas == bevy::render::texture::DEFAULT_IMAGE_HANDLE.typed() { - let mut prev_image = prev_image.clone(); - prev_image.data.clear(); - prev_image.data.extend_from_slice(pixels.as_slice()); - prev_image.resize(Extent3d { - width: widget_width as u32, - height: widget_height as u32, - depth_or_array_layers: 1, - }); + prev_image.data.clear(); + prev_image.data.extend_from_slice(pixels.as_slice()); + prev_image.resize(Extent3d { + width: size.0.x as u32, + height: size.0.y as u32, + depth_or_array_layers: 1, + }); + } + + cosmic_editor.0.buffer_mut().set_redraw(false); + } +} + +pub(crate) fn new_image_from_default( + mut query: Query<&mut Handle<Image>, Added<CosmicEditor>>, + mut images: ResMut<Assets<Image>>, +) { + for mut canvas in query.iter_mut() { + if let Some(prev_image) = images.get_mut(&canvas) { + if *canvas == DEFAULT_IMAGE_HANDLE.typed() { + let prev_image = prev_image.clone(); let handle_id: HandleId = HandleId::random::<Image>(); let new_handle: Handle<Image> = Handle::weak(handle_id); let new_handle = images.set(new_handle, prev_image); *canvas = new_handle; - } else { - prev_image.data.clear(); - prev_image.data.extend_from_slice(pixels.as_slice()); - prev_image.resize(Extent3d { - width: widget_width as u32, - height: widget_height as u32, - depth_or_array_layers: 1, - }); + } + } + } +} + +pub(crate) fn set_cursor( + mut query: Query<( + &mut XOffset, + &CosmicMode, + &CosmicEditor, + &CosmicWidgetSize, + &CosmicPadding, + )>, +) { + for (mut x_offset, mode, cosmic_editor, size, padding) in query.iter_mut() { + let mut cursor_x = 0.; + if mode == &CosmicMode::InfiniteLine { + if let Some(line) = cosmic_editor.0.buffer().layout_runs().next() { + for (idx, glyph) in line.glyphs.iter().enumerate() { + if cosmic_editor.0.cursor().affinity == Affinity::Before { + if idx <= cosmic_editor.0.cursor().index { + cursor_x += glyph.w; + } + } else if idx < cosmic_editor.0.cursor().index { + cursor_x += glyph.w; + } else { + break; + } + } } } - editor.buffer_mut().set_redraw(false); + if mode == &CosmicMode::InfiniteLine && x_offset.0.is_none() { + *x_offset = XOffset(Some((0., size.0.x - 2. * padding.0.x))); + } + + if let Some((x_min, x_max)) = x_offset.0 { + if cursor_x > x_max { + let diff = cursor_x - x_max; + *x_offset = XOffset(Some((x_min + diff, cursor_x))); + } + if cursor_x < x_min { + let diff = x_min - cursor_x; + *x_offset = XOffset(Some((cursor_x, x_max - diff))); + } + } } } +pub(crate) fn auto_height( + mut query: Query<( + Entity, + &mut Sprite, + &CosmicMode, + &mut CosmicEditor, + &CosmicWidgetSize, + )>, + mut style_q: Query<(&mut Style, &CosmicSource)>, + windows: Query<&Window, With<PrimaryWindow>>, +) { + let scale = windows.single().scale_factor() as f32; + + for (entity, mut sprite, mode, mut cosmic_editor, size) in query.iter_mut() { + if mode == &CosmicMode::AutoHeight { + let text_size = get_text_size(cosmic_editor.0.buffer()); + let text_height = (text_size.1 + 30.) / scale; + if text_height > size.0.y / scale { + let mut new_size = sprite.custom_size.unwrap(); + new_size.y = text_height.ceil(); + // TODO this gets set automatically in UI cases but needs to be done for all other cases. + // redundant work but easier to just set on all sprites + sprite.custom_size = Some(new_size); + + cosmic_editor.0.buffer_mut().set_redraw(true); + + // TODO: bad loop nesting + for (mut style, source) in style_q.iter_mut() { + if source.0 != entity { + continue; + } + style.height = Val::Px(text_height.ceil()); + } + } + } + } +} + +pub(crate) fn set_size_from_ui( + mut source_q: Query<&mut Sprite, With<CosmicEditor>>, + dest_q: Query<(&Node, &CosmicSource)>, +) { + 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)); + } + } +} + +pub(crate) fn _set_size_from_mesh() { + // TODO +} + fn draw_pixel( buffer: &mut [u8], width: i32, @@ -431,35 +503,27 @@ pub(crate) fn on_scale_factor_change( } } -pub(crate) fn cosmic_ui_to_canvas( - mut added_ui_images: Query<(&mut UiImage, &CosmicCanvas), Added<UiImage>>, -) { - for (mut ui_image, canvas) in added_ui_images.iter_mut() { - ui_image.texture = canvas.0.clone_weak(); - } -} - -pub(crate) fn update_handle_ui( - mut changed_handles: Query<(&mut UiImage, &CosmicCanvas), Changed<CosmicCanvas>>, -) { - for (mut ui_image, canvas) in changed_handles.iter_mut() { - ui_image.texture = canvas.0.clone_weak(); - } -} - -pub(crate) fn cosmic_sprite_to_canvas( - mut added_sprite_textures: Query<(&mut Handle<Image>, &CosmicCanvas), Added<Handle<Image>>>, -) { - for (mut handle, canvas) in added_sprite_textures.iter_mut() { - *handle = canvas.0.clone_weak(); - } -} - -pub(crate) fn update_handle_sprite( - mut changed_handles: Query<(&mut Handle<Image>, &CosmicCanvas), Changed<CosmicCanvas>>, +pub(crate) fn swap_target_handle( + source_q: Query<&Handle<Image>, (Changed<Handle<Image>>, With<CosmicEditor>)>, + mut dest_q: Query< + ( + Option<&mut Handle<Image>>, + Option<&mut UiImage>, + &CosmicSource, + ), + Without<CosmicEditor>, + >, ) { - for (mut handle, canvas) in changed_handles.iter_mut() { - *handle = canvas.0.clone_weak(); + // TODO: do this once + for (dest_handle_opt, dest_ui_opt, source_entity) in dest_q.iter_mut() { + if let Ok(source_handle) = source_q.get(source_entity.0) { + if let Some(mut dest_handle) = dest_handle_opt { + *dest_handle = source_handle.clone_weak(); + } + if let Some(mut dest_ui) = dest_ui_opt { + dest_ui.texture = source_handle.clone_weak(); + } + } } }