Skip to content
Snippets Groups Projects
Commit 9f66f493 authored by StarArawn's avatar StarArawn
Browse files

Fixed multiple issues:

- Text refusing to size correctly
- Fonts not loading correctly with new bevy context.
parent d1dfe663
No related branches found
No related tags found
No related merge requests found
......@@ -2,6 +2,8 @@ use std::sync::{Arc, RwLock};
use kayak_core::context::KayakContext;
use crate::FontMapping;
pub struct BevyContext {
pub kayak_context: Arc<RwLock<KayakContext>>,
}
......
......@@ -26,7 +26,7 @@ impl Plugin for BevyKayakUIPlugin {
.add_plugin(render::BevyKayakUIRenderPlugin)
.add_plugin(camera::KayakUICameraPlugin)
.add_system(update_window_size)
.add_system(process_events)
.add_system(process_events.exclusive_system())
.add_system(update.exclusive_system());
}
}
......@@ -46,61 +46,76 @@ pub fn update(world: &mut World) {
}
}
pub fn process_events(
bevy_context: Option<Res<BevyContext>>,
windows: Res<Windows>,
mut cursor_moved_events: EventReader<CursorMoved>,
mut mouse_button_input_events: EventReader<MouseButtonInput>,
mut char_input_events: EventReader<ReceivedCharacter>,
mut keyboard_input_events: EventReader<KeyboardInput>,
) {
let window_size = if let Some(window) = windows.get_primary() {
Vec2::new(window.width(), window.height())
pub fn process_events(world: &mut World) {
let window_size = if let Some(windows) = world.get_resource::<Windows>() {
if let Some(window) = windows.get_primary() {
Vec2::new(window.width(), window.height())
} else {
panic!("Couldn't find primary window!");
}
} else {
panic!("Couldn't find primary window!");
};
if let Some(bevy_context) = bevy_context {
if let Some(bevy_context) = world.remove_resource::<BevyContext>() {
if let Ok(mut context) = bevy_context.kayak_context.write() {
let mut input_events = Vec::new();
if let Some(event) = cursor_moved_events.iter().last() {
// Currently, we can only handle a single MouseMoved event at a time so everything but the last needs to be skipped
input_events.push(InputEvent::MouseMoved((
event.position.x as f32,
window_size.y - event.position.y as f32,
)));
}
for event in mouse_button_input_events.iter() {
match event.button {
MouseButton::Left => {
if event.state == ElementState::Pressed {
input_events.push(InputEvent::MouseLeftPress);
} else if event.state == ElementState::Released {
input_events.push(InputEvent::MouseLeftRelease);
context.set_global_state(std::mem::take(world));
context.query_world::<(
EventReader<CursorMoved>,
EventReader<MouseButtonInput>,
EventReader<ReceivedCharacter>,
EventReader<KeyboardInput>,
), _, _>(
|(
mut cursor_moved_events,
mut mouse_button_input_events,
mut char_input_events,
mut keyboard_input_events,
)| {
if let Some(event) = cursor_moved_events.iter().last() {
// Currently, we can only handle a single MouseMoved event at a time so everything but the last needs to be skipped
input_events.push(InputEvent::MouseMoved((
event.position.x as f32,
window_size.y - event.position.y as f32,
)));
}
for event in mouse_button_input_events.iter() {
match event.button {
MouseButton::Left => {
if event.state == ElementState::Pressed {
input_events.push(InputEvent::MouseLeftPress);
} else if event.state == ElementState::Released {
input_events.push(InputEvent::MouseLeftRelease);
}
}
_ => {}
}
}
_ => {}
}
}
for event in char_input_events.iter() {
input_events.push(InputEvent::CharEvent { c: event.char });
}
for event in keyboard_input_events.iter() {
if let Some(key_code) = event.key_code {
let kayak_key_code = key::convert_virtual_key_code(key_code);
input_events.push(InputEvent::Keyboard {
key: kayak_key_code,
is_pressed: matches!(event.state, ElementState::Pressed),
});
}
}
for event in char_input_events.iter() {
input_events.push(InputEvent::CharEvent { c: event.char });
}
for event in keyboard_input_events.iter() {
if let Some(key_code) = event.key_code {
let kayak_key_code = key::convert_virtual_key_code(key_code);
input_events.push(InputEvent::Keyboard {
key: kayak_key_code,
is_pressed: matches!(event.state, ElementState::Pressed),
});
}
}
},
);
context.process_events(input_events);
*world = context.take_global_state::<World>().unwrap()
}
world.insert_resource(bevy_context);
}
}
......
......@@ -32,6 +32,11 @@ impl FontMapping {
}
}
pub(crate) fn mark_all_as_new(&mut self) {
self.new_fonts
.extend(self.font_handles.keys().map(|key| key.clone()));
}
pub fn get_handle(&self, id: String) -> Option<Handle<KayakFont>> {
self.font_handles
.get(&id)
......
......@@ -41,6 +41,9 @@ fn process_loaded_fonts(
bevy_context: Option<Res<BevyContext>>,
) {
if let Some(context) = bevy_context {
if context.is_added() {
font_mapping.mark_all_as_new();
}
font_mapping.add_loaded_to_kayak(&fonts, &context);
}
}
......
use bevy::{
input::Input,
prelude::{App as BevyApp, AssetServer, Commands, KeyCode, Res, ResMut, State, SystemSet},
prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut, State, SystemSet},
window::WindowDescriptor,
DefaultPlugins,
};
use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
use kayak_ui::core::{render, Index};
use kayak_ui::core::{
render, rsx, widget, Event, EventType, Index, KayakContextRef, KeyCode, OnEvent, WidgetProps,
};
use kayak_ui::widgets::{App, Text};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
......@@ -15,11 +16,42 @@ enum GameState {
Play,
}
fn swap(mut state: ResMut<State<GameState>>) {
if *state.current() == GameState::MainMenu {
let _ = state.set(GameState::Options);
} else if *state.current() == GameState::Options {
let _ = state.set(GameState::Play);
} else {
let _ = state.set(GameState::MainMenu);
}
}
fn handle_input(context: &mut KayakContextRef, event: &mut Event) {
match event.event_type {
EventType::KeyDown(event) => {
if event.key() == KeyCode::Space {
context.query_world::<ResMut<State<GameState>>, _, _>(swap);
}
}
_ => {}
};
}
#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
pub struct StateSwitcherProps;
#[widget]
fn StateSwitcher(props: StateSwitcherProps) {
rsx! {
<Text content={"Press space to switch states!".to_string()} size={32.0} />
}
}
fn create_main_menu(mut commands: Commands) {
let context = BevyContext::new(|context| {
render! {
<App>
<App on_event={Some(OnEvent::new(handle_input))}>
<Text content={"Main Menu".to_string()} size={32.0} />
<StateSwitcher />
</App>
}
});
......@@ -30,8 +62,9 @@ fn create_main_menu(mut commands: Commands) {
fn create_options_menu(mut commands: Commands) {
let context = BevyContext::new(|context| {
render! {
<App>
<App on_event={Some(OnEvent::new(handle_input))}>
<Text content={"Options".to_string()} size={32.0} />
<StateSwitcher />
</App>
}
});
......@@ -42,8 +75,9 @@ fn create_options_menu(mut commands: Commands) {
fn create_play_menu(mut commands: Commands) {
let context = BevyContext::new(|context| {
render! {
<App>
<App on_event={Some(OnEvent::new(handle_input))}>
<Text content={"Play".to_string()} size={32.0} />
<StateSwitcher />
</App>
}
});
......@@ -64,19 +98,6 @@ fn startup(
fn destroy(mut commands: Commands) {
commands.remove_resource::<BevyContext>();
}
fn swap(mut state: ResMut<State<GameState>>, keys: Res<Input<KeyCode>>) {
if keys.just_pressed(KeyCode::Space) {
if *state.current() == GameState::MainMenu {
let _ = state.set(GameState::Options);
} else if *state.current() == GameState::Options {
let _ = state.set(GameState::Play);
} else {
let _ = state.set(GameState::MainMenu);
}
}
}
fn main() {
BevyApp::new()
.insert_resource(WindowDescriptor {
......@@ -95,6 +116,5 @@ fn main() {
.add_system_set(SystemSet::on_exit(GameState::Options).with_system(destroy))
.add_system_set(SystemSet::on_enter(GameState::Play).with_system(create_play_menu))
.add_system_set(SystemSet::on_exit(GameState::Play).with_system(destroy))
.add_system(swap)
.run();
}
......@@ -260,6 +260,14 @@ impl<'a> KayakContextRef<'a> {
.tree
.merge(&tree, self.current_id.unwrap_or_default(), changes);
}
/// This function is specifically for text rendering which needs to re-render when
/// it's parent layout is calculated.
pub fn mark_dirty(&mut self, widget_id: &Index) {
if let Ok(mut dirty_nodes) = self.context.widget_manager.dirty_nodes.lock() {
dirty_nodes.insert(*widget_id);
}
}
}
#[test]
......
......@@ -36,6 +36,8 @@ pub fn Text(props: TextProps) {
context.bind(&font);
let mut should_render = true;
// TODO: It might be worth caching the measurement here until content changes.
let (layout_size, parent_size) =
if let Some(parent_id) = context.get_valid_parent(parent_id.unwrap()) {
......@@ -48,30 +50,36 @@ pub fn Text(props: TextProps) {
line_height.unwrap_or(size * 1.2),
(layout.width, layout.height),
);
(measurement, (layout.width, layout.height))
} else {
should_render = false;
((0.0, 0.0), (layout.width, layout.height))
}
} else {
should_render = false;
((0.0, 0.0), (0.0, 0.0))
}
} else {
should_render = false;
((0.0, 0.0), (0.0, 0.0))
};
let render_command = RenderCommand::Text {
content: content.clone(),
size,
parent_size,
line_height: line_height.unwrap_or(size * 1.2),
font: font_name.clone().unwrap_or("Roboto".into()),
};
if should_render {
let render_command = RenderCommand::Text {
content: content.clone(),
size,
parent_size,
line_height: line_height.unwrap_or(size * 1.2),
font: font_name.clone().unwrap_or("Roboto".into()),
};
props.styles = Some(Style {
render_command: StyleProp::Value(render_command),
width: StyleProp::Value(Units::Pixels(layout_size.0)),
height: StyleProp::Value(Units::Pixels(layout_size.1)),
..props.styles.clone().unwrap_or_default()
});
props.styles = Some(Style {
render_command: StyleProp::Value(render_command),
width: StyleProp::Value(Units::Pixels(layout_size.0)),
height: StyleProp::Value(Units::Pixels(layout_size.1)),
..props.styles.clone().unwrap_or_default()
});
} else {
context.mark_dirty(&self.get_id());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment