Skip to content
Snippets Groups Projects
Commit 3ff1150d authored by StarToaster's avatar StarToaster
Browse files

Cursors + text boxes.

parent 0b25cb59
No related branches found
No related tags found
No related merge requests found
...@@ -219,6 +219,7 @@ fn create_primitive( ...@@ -219,6 +219,7 @@ fn create_primitive(
font, font,
properties, properties,
text_layout, text_layout,
word_wrap,
.. ..
} => { } => {
// --- Bind to Font Asset --- // // --- Bind to Font Asset --- //
...@@ -247,6 +248,11 @@ fn create_primitive( ...@@ -247,6 +248,11 @@ fn create_primitive(
parent_layout.height - border_y, parent_layout.height - border_y,
); );
// TODO: Fix this hack.
if !*word_wrap {
properties.max_size.0 = 100000.0;
}
needs_layout = false; needs_layout = false;
if properties.max_size.0 == 0.0 || properties.max_size.1 == 0.0 { if properties.max_size.0 == 0.0 || properties.max_size.1 == 0.0 {
......
...@@ -25,6 +25,7 @@ pub enum RenderPrimitive { ...@@ -25,6 +25,7 @@ pub enum RenderPrimitive {
text_layout: TextLayout, text_layout: TextLayout,
layout: Rect, layout: Rect,
properties: TextProperties, properties: TextProperties,
word_wrap: bool,
}, },
Image { Image {
border_radius: Corner<f32>, border_radius: Corner<f32>,
...@@ -103,7 +104,11 @@ impl From<&KStyle> for RenderPrimitive { ...@@ -103,7 +104,11 @@ impl From<&KStyle> for RenderPrimitive {
border: style.border.resolve(), border: style.border.resolve(),
layout: Rect::default(), layout: Rect::default(),
}, },
RenderCommand::Text { content, alignment } => Self::Text { RenderCommand::Text {
content,
alignment,
word_wrap,
} => Self::Text {
color: style.color.resolve(), color: style.color.resolve(),
content, content,
font, font,
...@@ -115,6 +120,7 @@ impl From<&KStyle> for RenderPrimitive { ...@@ -115,6 +120,7 @@ impl From<&KStyle> for RenderPrimitive {
alignment, alignment,
..Default::default() ..Default::default()
}, },
word_wrap,
}, },
RenderCommand::Image { handle } => Self::Image { RenderCommand::Image { handle } => Self::Image {
border_radius: style.border_radius.resolve(), border_radius: style.border_radius.resolve(),
......
...@@ -13,6 +13,7 @@ pub enum RenderCommand { ...@@ -13,6 +13,7 @@ pub enum RenderCommand {
Text { Text {
content: String, content: String,
alignment: Alignment, alignment: Alignment,
word_wrap: bool,
}, },
Image { Image {
handle: Handle<Image>, handle: Handle<Image>,
......
...@@ -33,6 +33,9 @@ pub struct TextProps { ...@@ -33,6 +33,9 @@ pub struct TextProps {
pub alignment: Alignment, pub alignment: Alignment,
/// Custom styles to pass in. /// Custom styles to pass in.
pub user_styles: KStyle, pub user_styles: KStyle,
/// Basic word wrapping.
/// Defautls to true
pub word_wrap: bool,
} }
impl Default for TextProps { impl Default for TextProps {
...@@ -44,6 +47,7 @@ impl Default for TextProps { ...@@ -44,6 +47,7 @@ impl Default for TextProps {
show_cursor: false, show_cursor: false,
size: -1.0, size: -1.0,
alignment: Alignment::Start, alignment: Alignment::Start,
word_wrap: true,
user_styles: Default::default(), user_styles: Default::default(),
} }
} }
...@@ -84,6 +88,7 @@ pub fn text_render( ...@@ -84,6 +88,7 @@ pub fn text_render(
render_command: StyleProp::Value(RenderCommand::Text { render_command: StyleProp::Value(RenderCommand::Text {
content: text.content.clone(), content: text.content.clone(),
alignment: text.alignment, alignment: text.alignment,
word_wrap: text.word_wrap,
}), }),
font: if let Some(ref font) = text.font { font: if let Some(ref font) = text.font {
StyleProp::Value(font.clone()) StyleProp::Value(font.clone())
......
...@@ -12,7 +12,7 @@ use crate::{ ...@@ -12,7 +12,7 @@ use crate::{
on_layout::OnLayout, on_layout::OnLayout,
prelude::{KChildren, KayakWidgetContext, OnChange}, prelude::{KChildren, KayakWidgetContext, OnChange},
render::font::FontMapping, render::font::FontMapping,
styles::{Edge, KStyle, RenderCommand, StyleProp, Units}, styles::{Edge, KPositionType, KStyle, RenderCommand, StyleProp, Units},
widget::Widget, widget::Widget,
widget_state::WidgetState, widget_state::WidgetState,
widgets::{ widgets::{
...@@ -22,6 +22,8 @@ use crate::{ ...@@ -22,6 +22,8 @@ use crate::{
Focusable, DEFAULT_FONT, Focusable, DEFAULT_FONT,
}; };
use super::ElementBundle;
/// Props used by the [`TextBox`] widget /// Props used by the [`TextBox`] widget
#[derive(Component, PartialEq, Default, Debug, Clone)] #[derive(Component, PartialEq, Default, Debug, Clone)]
pub struct TextBoxProps { pub struct TextBoxProps {
...@@ -44,6 +46,7 @@ pub struct TextBoxState { ...@@ -44,6 +46,7 @@ pub struct TextBoxState {
pub cursor_position: usize, pub cursor_position: usize,
pub cursor_visible: bool, pub cursor_visible: bool,
pub cursor_last_update: Instant, pub cursor_last_update: Instant,
pub current_value: String,
} }
impl Default for TextBoxState { impl Default for TextBoxState {
...@@ -55,6 +58,7 @@ impl Default for TextBoxState { ...@@ -55,6 +58,7 @@ impl Default for TextBoxState {
cursor_position: Default::default(), cursor_position: Default::default(),
cursor_visible: Default::default(), cursor_visible: Default::default(),
cursor_last_update: Instant::now(), cursor_last_update: Instant::now(),
current_value: String::new(),
} }
} }
} }
...@@ -96,18 +100,34 @@ pub fn text_box_render( ...@@ -96,18 +100,34 @@ pub fn text_box_render(
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
mut query: Query<(&mut KStyle, &TextBoxProps, &mut OnEvent, &OnChange)>, mut query: Query<(&mut KStyle, &TextBoxProps, &mut OnEvent, &OnChange)>,
state_query: Query<&TextBoxState>, mut state_query: ParamSet<(Query<&TextBoxState>, Query<&mut TextBoxState>)>,
font_assets: Res<Assets<KayakFont>>,
font_mapping: Res<FontMapping>,
) -> bool { ) -> bool {
if let Ok((mut styles, text_box, mut on_event, on_change)) = query.get_mut(entity) { if let Ok((mut styles, text_box, mut on_event, on_change)) = query.get_mut(entity) {
let state_entity = widget_context.use_state::<TextBoxState>( let state_entity = widget_context.use_state::<TextBoxState>(
&mut commands, &mut commands,
entity, entity,
TextBoxState { TextBoxState {
current_value: text_box.value.clone(),
..TextBoxState::default() ..TextBoxState::default()
}, },
); );
if let Ok(state) = state_query.get(state_entity) { let mut is_different = false;
if let Ok(state) = state_query.p0().get(state_entity) {
if state.current_value != text_box.value {
is_different = true;
}
}
if is_different {
if let Ok(mut state) = state_query.p1().get_mut(state_entity) {
state.current_value = text_box.value.clone();
}
}
if let Ok(state) = state_query.p0().get(state_entity) {
*styles = KStyle::default() *styles = KStyle::default()
// Required styles // Required styles
.with_style(KStyle { .with_style(KStyle {
...@@ -140,9 +160,7 @@ pub fn text_box_render( ...@@ -140,9 +160,7 @@ pub fn text_box_render(
..Default::default() ..Default::default()
}; };
let current_value = text_box.value.clone();
let cloned_on_change = on_change.clone(); let cloned_on_change = on_change.clone();
let style_font = styles.font.clone(); let style_font = styles.font.clone();
*on_event = OnEvent::new( *on_event = OnEvent::new(
...@@ -185,40 +203,26 @@ pub fn text_box_render( ...@@ -185,40 +203,26 @@ pub fn text_box_render(
} }
} }
EventType::CharInput { c } => { EventType::CharInput { c } => {
let mut current_value = current_value.clone(); if let Ok(mut state) = state_query.get_mut(state_entity) {
let cloned_on_change = cloned_on_change.clone(); let cloned_on_change = cloned_on_change.clone();
if let Ok(state) = state_query.get(state_entity) {
if !state.focused { if !state.focused {
return (event_dispatcher_context, event); return (event_dispatcher_context, event);
} }
} else { let cursor_pos = state.cursor_position;
return (event_dispatcher_context, event); if is_backspace(c) {
} if !state.current_value.is_empty() {
if is_backspace(c) {
if !current_value.is_empty() {
if let Ok(mut state) = state_query.get_mut(state_entity) {
// TODO: This doesn't respect graphemes! // TODO: This doesn't respect graphemes!
current_value.remove(state.cursor_position - 1); state.current_value.remove(cursor_pos - 1);
state.cursor_position -= 1; state.cursor_position -= 1;
} }
} } else if !c.is_control() {
} else if !c.is_control() {
if let Ok(mut state) = state_query.get_mut(state_entity) {
// TODO: This doesn't respect graphemes! // TODO: This doesn't respect graphemes!
current_value.insert(state.cursor_position, c); state.current_value.insert(cursor_pos, c);
state.cursor_position += 1; state.cursor_position += 1;
} }
}
if let Ok(mut state) = state_query.get_mut(state_entity) {
// Update graphemes // Update graphemes
set_graphemes( set_graphemes(&mut state, &font_assets, &font_mapping, &style_font);
&mut state,
&font_assets,
&font_mapping,
&style_font,
&current_value,
);
set_new_cursor_position( set_new_cursor_position(
&mut state, &mut state,
...@@ -226,22 +230,15 @@ pub fn text_box_render( ...@@ -226,22 +230,15 @@ pub fn text_box_render(
&font_mapping, &font_mapping,
&style_font, &style_font,
); );
cloned_on_change.set_value(state.current_value.clone());
event.add_system(cloned_on_change);
} }
cloned_on_change.set_value(current_value);
event.add_system(cloned_on_change);
} }
EventType::Focus => { EventType::Focus => {
if let Ok(mut state) = state_query.get_mut(state_entity) { if let Ok(mut state) = state_query.get_mut(state_entity) {
state.focused = true; state.focused = true;
// Update graphemes // Update graphemes
set_graphemes( set_graphemes(&mut state, &font_assets, &font_mapping, &style_font);
&mut state,
&font_assets,
&font_mapping,
&style_font,
&current_value,
);
state.cursor_position = state.graphemes.len(); state.cursor_position = state.graphemes.len();
...@@ -266,7 +263,7 @@ pub fn text_box_render( ...@@ -266,7 +263,7 @@ pub fn text_box_render(
let cursor_styles = KStyle { let cursor_styles = KStyle {
background_color: Color::rgba(0.933, 0.745, 0.745, 1.0).into(), background_color: Color::rgba(0.933, 0.745, 0.745, 1.0).into(),
position_type: crate::styles::KPositionType::SelfDirected.into(), position_type: KPositionType::SelfDirected.into(),
top: Units::Pixels(5.0).into(), top: Units::Pixels(5.0).into(),
left: Units::Pixels(state.cursor_x).into(), left: Units::Pixels(state.cursor_x).into(),
width: Units::Pixels(2.0).into(), width: Units::Pixels(2.0).into(),
...@@ -274,6 +271,51 @@ pub fn text_box_render( ...@@ -274,6 +271,51 @@ pub fn text_box_render(
..Default::default() ..Default::default()
}; };
let text_styles = KStyle {
top: Units::Stretch(1.0).into(),
bottom: Units::Stretch(1.0).into(),
..Default::default()
};
let shift = if let Some(layout) = widget_context.get_layout(entity) {
let font_handle = match &styles.font {
StyleProp::Value(font) => font_mapping.get_handle(font.clone()).unwrap(),
_ => font_mapping.get_handle(DEFAULT_FONT.into()).unwrap(),
};
if let Some(font) = font_assets.get(&font_handle) {
let string_to_cursor = state.graphemes[0..state.cursor_position].join("");
let measurement = font.measure(
&string_to_cursor,
TextProperties {
font_size: 14.0,
line_height: 18.0,
max_size: (10000.0, 18.0),
alignment: kayak_font::Alignment::Start,
tab_size: 4,
},
);
if measurement.size().0 > layout.width {
(layout.width - measurement.size().0) - 20.0
} else {
0.0
}
} else {
0.0
}
} else {
0.0
};
let scroll_styles = KStyle {
position_type: KPositionType::SelfDirected.into(),
padding_left: StyleProp::Value(Units::Stretch(0.0)),
padding_right: StyleProp::Value(Units::Stretch(0.0)),
padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
padding_top: StyleProp::Value(Units::Stretch(1.0)),
left: Units::Pixels(shift).into(),
..Default::default()
};
let parent_id = Some(entity); let parent_id = Some(entity);
rsx! { rsx! {
<BackgroundBundle styles={background_styles}> <BackgroundBundle styles={background_styles}>
...@@ -281,30 +323,27 @@ pub fn text_box_render( ...@@ -281,30 +323,27 @@ pub fn text_box_render(
height: Units::Pixels(26.0).into(), height: Units::Pixels(26.0).into(),
padding_left: StyleProp::Value(Units::Stretch(0.0)), padding_left: StyleProp::Value(Units::Stretch(0.0)),
padding_right: StyleProp::Value(Units::Stretch(0.0)), padding_right: StyleProp::Value(Units::Stretch(0.0)),
padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
padding_top: StyleProp::Value(Units::Stretch(1.0)),
..Default::default() ..Default::default()
}}> }}>
<TextWidgetBundle <ElementBundle styles={scroll_styles}>
text={TextProps { <TextWidgetBundle
content: text_box.value.clone(), text={TextProps {
size: 14.0, content: text_box.value.clone(),
line_height: Some(18.0), size: 14.0,
user_styles: KStyle { line_height: Some(18.0),
top: Units::Stretch(1.0).into(), user_styles: text_styles,
bottom: Units::Stretch(1.0).into(), word_wrap: false,
..Default::default() ..Default::default()
}, }}
..Default::default() />
}} {
/> if state.focused && state.cursor_visible {
{ constructor! {
if state.focused && state.cursor_visible { <BackgroundBundle styles={cursor_styles} />
constructor! { }
<BackgroundBundle styles={cursor_styles} />
} }
} }
} </ElementBundle>
</ClipBundle> </ClipBundle>
</BackgroundBundle> </BackgroundBundle>
} }
...@@ -326,7 +365,6 @@ fn set_graphemes( ...@@ -326,7 +365,6 @@ fn set_graphemes(
font_assets: &Res<Assets<KayakFont>>, font_assets: &Res<Assets<KayakFont>>,
font_mapping: &FontMapping, font_mapping: &FontMapping,
style_font: &StyleProp<String>, style_font: &StyleProp<String>,
current_value: &String,
) { ) {
let font_handle = match style_font { let font_handle = match style_font {
StyleProp::Value(font) => font_mapping.get_handle(font.clone()).unwrap(), StyleProp::Value(font) => font_mapping.get_handle(font.clone()).unwrap(),
...@@ -335,7 +373,7 @@ fn set_graphemes( ...@@ -335,7 +373,7 @@ fn set_graphemes(
if let Some(font) = font_assets.get(&font_handle) { if let Some(font) = font_assets.get(&font_handle) {
state.graphemes = font state.graphemes = font
.get_graphemes(&current_value) .get_graphemes(&state.current_value)
.iter() .iter()
.map(|s| s.to_string()) .map(|s| s.to_string())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
......
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