Skip to content

Commit

Permalink
Allow setting placeholder text for text input (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
presiyan-ivanov authored Nov 20, 2023
1 parent 918b2e9 commit 18ef369
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 7 deletions.
19 changes: 16 additions & 3 deletions examples/widget-gallery/src/inputs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use floem::{
peniko::Color, reactive::create_rw_signal, style::CursorStyle, view::View, views::Decorators,
widgets::text_input,
cosmic_text::{self, Weight},
peniko::Color,
reactive::create_rw_signal,
style::CursorStyle,
view::View,
views::Decorators,
widgets::{text_input, PlaceholderTextClass},
};

use crate::form::{form, form_item};
Expand All @@ -11,10 +16,13 @@ pub fn text_input_view() -> impl View {
form({
(
form_item("Simple Input:".to_string(), 120.0, move || {
text_input(text).keyboard_navigatable()
text_input(text)
.placeholder("Placeholder text")
.keyboard_navigatable()
}),
form_item("Styled Input:".to_string(), 120.0, move || {
text_input(text)
.placeholder("Placeholder text")
.style(|s| {
s.border(1.5)
.width(250.0)
Expand All @@ -31,6 +39,11 @@ pub fn text_input_view() -> impl View {
s.border_color(Color::LIGHT_SKY_BLUE.with_alpha_factor(0.8))
.hover(|s| s.border_color(Color::LIGHT_SKY_BLUE))
})
.class(PlaceholderTextClass, |s| {
s.color(Color::LIGHT_SKY_BLUE)
.font_style(cosmic_text::Style::Italic)
.font_weight(Weight::BOLD)
})
})
.keyboard_navigatable()
}),
Expand Down
69 changes: 68 additions & 1 deletion src/views/text_input.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::action::exec_after;
use crate::keyboard::{self, KeyEvent};
use crate::reactive::{create_effect, RwSignal};
use crate::style::{CursorStyle, TextColor};
use crate::style::{CursorStyle, FontStyle, FontWeight, TextColor};
use crate::style::{FontProps, PaddingLeft};
use crate::unit::{PxPct, PxPctAuto};
use crate::view::ViewData;
use crate::widgets::PlaceholderTextClass;
use crate::{prop_extracter, EventPropagation};
use clipboard::{ClipboardContext, ClipboardProvider};
use taffy::prelude::{Layout, Node};
Expand Down Expand Up @@ -38,6 +39,15 @@ prop_extracter! {
}
}

prop_extracter! {
PlaceholderStyle {
pub color: TextColor,
//TODO: pub font_size: FontSize,
pub font_weight: FontWeight,
pub font_style: FontStyle,
}
}

enum InputKind {
SingleLine,
#[allow(unused)]
Expand All @@ -51,6 +61,9 @@ enum InputKind {
pub struct TextInput {
data: ViewData,
buffer: RwSignal<String>,
pub(crate) placeholder_text: Option<String>,
placeholder_buff: Option<TextLayout>,
placeholder_style: PlaceholderStyle,
// Where are we in the main buffer
cursor_glyph_idx: usize,
// This can be retrieved from the glyph, but we store it for efficiency
Expand Down Expand Up @@ -109,6 +122,9 @@ pub fn text_input(buffer: RwSignal<String>) -> TextInput {
TextInput {
data: ViewData::new(id),
cursor_glyph_idx: 0,
placeholder_text: None,
placeholder_buff: None,
placeholder_style: Default::default(),
buffer,
text_buf: None,
text_node: None,
Expand Down Expand Up @@ -387,6 +403,29 @@ impl TextInput {
self.font.size().unwrap_or(DEFAULT_FONT_SIZE)
}

pub fn get_placeholder_text_attrs(&self) -> AttrsList {
let mut attrs = Attrs::new().color(self.placeholder_style.color().unwrap_or(Color::BLACK));

//TODO:
// self.placeholder_style
// .font_size()
// .unwrap_or(self.font_size())
attrs = attrs.font_size(self.font_size());

if let Some(font_style) = self.placeholder_style.font_style() {
attrs = attrs.style(font_style);
} else if let Some(font_style) = self.font.style() {
attrs = attrs.style(font_style);
}

if let Some(font_weight) = self.placeholder_style.font_weight() {
attrs = attrs.weight(font_weight);
} else if let Some(font_weight) = self.font.weight() {
attrs = attrs.weight(font_weight);
}
AttrsList::new(attrs)
}

pub fn get_text_attrs(&self) -> AttrsList {
let mut attrs = Attrs::new().color(self.style.color().unwrap_or(Color::BLACK));

Expand Down Expand Up @@ -684,6 +723,18 @@ impl TextInput {
self.selection = Some(new_selection);
}
}

fn paint_placeholder_text(
&self,
placeholder_buff: &TextLayout,
cx: &mut crate::context::PaintCx,
) {
let text_node = self.text_node.unwrap();
let layout = *cx.app_state.taffy.layout(text_node).unwrap();
let node_location = layout.location;
let text_start_point = Point::new(node_location.x as f64, node_location.y as f64);
cx.draw_text(placeholder_buff, text_start_point);
}
}

fn replace_range(buff: &mut String, del_range: Range<usize>, replacement: Option<&str>) {
Expand Down Expand Up @@ -803,13 +854,17 @@ impl View for TextInput {
}

fn style(&mut self, cx: &mut crate::context::StyleCx<'_>) {
let style = cx.style();
if self.font.read(cx) || self.text_buf.is_none() {
self.update_text_layout();
cx.app_state_mut().request_layout(self.id());
}
if self.style.read(cx) {
cx.app_state_mut().request_paint(self.id());
}

let placeholder_style = style.clone().apply_class(PlaceholderTextClass);
self.placeholder_style.read_style(cx, &placeholder_style);
}

fn layout(&mut self, cx: &mut crate::context::LayoutCx) -> taffy::prelude::Node {
Expand All @@ -831,6 +886,15 @@ impl View for TextInput {
let style = cx.app_state_mut().get_builtin_style(self.id());
let node_width = layout.size.width;

if self.placeholder_buff.is_none() {
if let Some(placeholder_text) = &self.placeholder_text {
let mut placeholder_buff = TextLayout::new();
let attrs_list = self.get_placeholder_text_attrs();
placeholder_buff.set_text(placeholder_text, attrs_list);
self.placeholder_buff = Some(placeholder_buff);
}
}

let style_width = style.width();
let width_px = match style_width {
crate::unit::PxPctAuto::Px(px) => px as f32,
Expand Down Expand Up @@ -873,6 +937,9 @@ impl View for TextInput {
if !cx.app_state.is_focused(&self.id())
&& self.buffer.with_untracked(|buff| buff.is_empty())
{
if let Some(placeholder_buff) = &self.placeholder_buff {
self.paint_placeholder_text(placeholder_buff, cx);
}
return;
}

Expand Down
4 changes: 4 additions & 0 deletions src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ pub(crate) fn default_theme() -> Theme {
.set(slider::CircleRad, PxPct::Pct(100.))
.set(slider::BarExtends, false)
})
.class(PlaceholderTextClass, |s| {
s.color(Color::rgba8(158, 158, 158, 30))
.font_size(FONT_SIZE)
})
.class(TooltipClass, |s| {
s.border(0.5)
.border_color(Color::rgb8(140, 140, 140))
Expand Down
13 changes: 10 additions & 3 deletions src/widgets/text_input.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
use crate::{
style_class,
view::View,
views::{self, Decorators},
views::{self, Decorators, TextInput},
};
use floem_reactive::RwSignal;

style_class!(pub TextInputClass);
style_class!(pub PlaceholderTextClass);

pub fn text_input(buffer: RwSignal<String>) -> impl View {
pub fn text_input(buffer: RwSignal<String>) -> TextInput {
views::text_input(buffer).class(TextInputClass)
}

impl TextInput {
pub fn placeholder(mut self, text: impl Into<String>) -> Self {
self.placeholder_text = Some(text.into());
self
}
}

0 comments on commit 18ef369

Please sign in to comment.