1
0
Fork 0

Center the text entry field

This commit is contained in:
Robbert van der Helm 2022-03-15 13:06:05 +01:00
parent 61c60b5c73
commit 6144fc0011

View file

@ -2,14 +2,15 @@
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use nih_plug::prelude::{GuiContext, Param, ParamSetter}; use nih_plug::prelude::{GuiContext, Param, ParamSetter};
use std::borrow::Borrow;
use crate::backend::widget; use crate::backend::widget;
use crate::backend::Renderer; use crate::backend::Renderer;
use crate::renderer::Renderer as GraphicsRenderer; use crate::renderer::Renderer as GraphicsRenderer;
use crate::text::Renderer as TextRenderer; use crate::text::Renderer as TextRenderer;
use crate::{ use crate::{
alignment, event, keyboard, layout, mouse, renderer, text, touch, Clipboard, Color, Element, alignment, event, keyboard, layout, mouse, renderer, text, touch, Background, Clipboard, Color,
Event, Font, Layout, Length, Point, Rectangle, Shell, Size, TextInput, Vector, Widget, Element, Event, Font, Layout, Length, Point, Rectangle, Shell, Size, TextInput, Vector, Widget,
}; };
use super::util; use super::util;
@ -75,11 +76,16 @@ struct TextInputStyle;
impl widget::text_input::StyleSheet for TextInputStyle { impl widget::text_input::StyleSheet for TextInputStyle {
fn active(&self) -> widget::text_input::Style { fn active(&self) -> widget::text_input::Style {
widget::text_input::Style::default() widget::text_input::Style {
background: Background::Color(Color::TRANSPARENT),
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
} }
fn focused(&self) -> widget::text_input::Style { fn focused(&self) -> widget::text_input::Style {
widget::text_input::Style::default() self.active()
} }
fn placeholder_color(&self) -> Color { fn placeholder_color(&self) -> Color {
@ -175,7 +181,11 @@ impl<'a, P: Param> Widget<ParamMessage, Renderer> for ParamSlider<'a, P> {
let event = event.clone(); let event = event.clone();
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut shell = Shell::new(&mut messages); let mut shell = Shell::new(&mut messages);
let status = self.with_text_input(layout, current_value, |mut text_input, layout| { let status = self.with_text_input(
layout,
renderer,
current_value,
|mut text_input, layout, renderer| {
text_input.on_event( text_input.on_event(
event, event,
layout, layout,
@ -184,7 +194,8 @@ impl<'a, P: Param> Widget<ParamMessage, Renderer> for ParamSlider<'a, P> {
clipboard, clipboard,
&mut shell, &mut shell,
) )
}); },
);
// Pressing escape will unfocus the text field, so we should propagate that change in // Pressing escape will unfocus the text field, so we should propagate that change in
// our own model // our own model
@ -384,6 +395,18 @@ impl<'a, P: Param> Widget<ParamMessage, Renderer> for ParamSlider<'a, P> {
background_color, background_color,
); );
// Only draw the text input widget when it gets focussed. Otherwise, overlay the label with
// the slider.
if let Some(current_value) = &self.state.text_input_value {
self.with_text_input(
layout,
renderer,
current_value,
|text_input, layout, renderer| {
text_input.draw(renderer, layout, cursor_position, None)
},
)
} else {
// We'll visualize the difference between the current value and the default value // We'll visualize the difference between the current value and the default value
let current_value = self.param.normalized_value(); let current_value = self.param.normalized_value();
let fill_start_x = util::remap_rect_x_t( let fill_start_x = util::remap_rect_x_t(
@ -409,14 +432,8 @@ impl<'a, P: Param> Widget<ParamMessage, Renderer> for ParamSlider<'a, P> {
fill_color, fill_color,
); );
// Only draw the text input widget when it gets focussed. Otherwise, overlay the label with // To make it more readable (and because it looks cool), the parts that overlap with the
// the slider. To make it more readable (and because it looks cool), the parts that overlap // fill rect will be rendered in white while the rest will be rendered in black.
// with the fill rect will be rendered in white while the rest will be rendered in black.
if let Some(current_value) = &self.state.text_input_value {
self.with_text_input(layout, current_value, |text_input, layout| {
text_input.draw(renderer, layout, cursor_position, None)
})
} else {
let display_value = self.param.to_string(); let display_value = self.param.to_string();
let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()) as f32; let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()) as f32;
let text_bounds = Rectangle { let text_bounds = Rectangle {
@ -466,42 +483,49 @@ impl<'a, P: Param> ParamSlider<'a, P> {
/// Create a temporary [`TextInput`] hooked up to [`State::text_input_value`] and outputting /// Create a temporary [`TextInput`] hooked up to [`State::text_input_value`] and outputting
/// [`TextInputMessage`] messages and do something with it. This can be used to /// [`TextInputMessage`] messages and do something with it. This can be used to
fn with_text_input<R, F: FnOnce(TextInput<'_, TextInputMessage>, Layout) -> R>( fn with_text_input<T, R, F>(&self, layout: Layout, renderer: R, current_value: &str, f: F) -> T
&self, where
layout: Layout, F: FnOnce(TextInput<'_, TextInputMessage>, Layout, R) -> T,
current_value: &str, R: Borrow<Renderer>,
f: F, {
) -> R {
let mut text_input_state = self.state.text_input_state.borrow_mut(); let mut text_input_state = self.state.text_input_state.borrow_mut();
text_input_state.focus(); text_input_state.focus();
let text_size = self
.text_size
.unwrap_or_else(|| renderer.borrow().default_size());
let text_width = renderer
.borrow()
.measure_width(current_value, text_size, self.font);
let text_input = TextInput::new( let text_input = TextInput::new(
&mut text_input_state, &mut text_input_state,
"", "",
current_value, current_value,
TextInputMessage::Value, TextInputMessage::Value,
) )
.width(self.width) .font(self.font)
.size(text_size)
.width(Length::Units(text_width.ceil() as u16))
.style(TextInputStyle) .style(TextInputStyle)
.on_submit(TextInputMessage::Submit); .on_submit(TextInputMessage::Submit);
// Make sure to not draw over the borders // Make sure to not draw over the borders, and center the text
let offset_node = layout::Node::with_children( let offset_node = layout::Node::with_children(
Size { Size {
width: layout.bounds().size().width - (BORDER_WIDTH * 2.0), width: text_width,
height: layout.bounds().size().height - (BORDER_WIDTH * 2.0), height: layout.bounds().size().height - (BORDER_WIDTH * 2.0),
}, },
vec![layout::Node::new(layout.bounds().size())], vec![layout::Node::new(layout.bounds().size())],
); );
let offset_layout = Layout::with_offset( let offset_layout = Layout::with_offset(
Vector { Vector {
x: layout.position().x + BORDER_WIDTH, x: layout.bounds().center_x() - (text_width / 2.0),
y: layout.position().y + BORDER_WIDTH, y: layout.position().y + BORDER_WIDTH,
}, },
&offset_node, &offset_node,
); );
f(text_input, offset_layout) f(text_input, offset_layout, renderer)
} }
/// Set the normalized value for a parameter if that would change the parameter's plain value /// Set the normalized value for a parameter if that would change the parameter's plain value