Merge branch 'master' into advanced-text

This commit is contained in:
Héctor Ramón Jiménez 2023-04-17 23:41:12 +02:00
commit 4bae457c37
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
73 changed files with 1586 additions and 703 deletions

View file

@ -134,8 +134,8 @@ mod numeric_input {
.map(u32::to_string)
.as_deref()
.unwrap_or(""),
Event::InputChanged,
)
.on_input(Event::InputChanged)
.padding(10),
button("+", Event::IncrementPressed),
]

View file

@ -18,10 +18,7 @@ pub struct Download<I> {
url: String,
}
async fn download<I: Copy>(
id: I,
state: State,
) -> (Option<(I, Progress)>, State) {
async fn download<I: Copy>(id: I, state: State) -> ((I, Progress), State) {
match state {
State::Ready(url) => {
let response = reqwest::get(&url).await;
@ -30,7 +27,7 @@ async fn download<I: Copy>(
Ok(response) => {
if let Some(total) = response.content_length() {
(
Some((id, Progress::Started)),
(id, Progress::Started),
State::Downloading {
response,
total,
@ -38,10 +35,10 @@ async fn download<I: Copy>(
},
)
} else {
(Some((id, Progress::Errored)), State::Finished)
((id, Progress::Errored), State::Finished)
}
}
Err(_) => (Some((id, Progress::Errored)), State::Finished),
Err(_) => ((id, Progress::Errored), State::Finished),
}
}
State::Downloading {
@ -55,7 +52,7 @@ async fn download<I: Copy>(
let percentage = (downloaded as f32 / total as f32) * 100.0;
(
Some((id, Progress::Advanced(percentage))),
(id, Progress::Advanced(percentage)),
State::Downloading {
response,
total,
@ -63,8 +60,8 @@ async fn download<I: Copy>(
},
)
}
Ok(None) => (Some((id, Progress::Finished)), State::Finished),
Err(_) => (Some((id, Progress::Errored)), State::Finished),
Ok(None) => ((id, Progress::Finished), State::Finished),
Err(_) => ((id, Progress::Errored), State::Finished),
},
State::Finished => {
// We do not let the stream die, as it would start a

View file

@ -10,7 +10,7 @@ iced_winit = { path = "../../winit" }
iced_wgpu = { path = "../../wgpu" }
iced_widget = { path = "../../widget" }
iced_renderer = { path = "../../renderer", features = ["wgpu", "tiny-skia"] }
env_logger = "0.8"
env_logger = "0.10"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.7"

View file

@ -102,11 +102,10 @@ impl Program for Controls {
.size(14)
.style(Color::WHITE),
)
.push(text_input(
"Placeholder",
text,
Message::TextChanged,
)),
.push(
text_input("Placeholder", text)
.on_input(Message::TextChanged),
),
),
)
.into()

View file

@ -30,6 +30,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(target_arch = "wasm32")]
let canvas_element = {
console_log::init_with_level(log::Level::Debug)?;
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
web_sys::window()
@ -49,7 +50,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
.build(&event_loop)?;
#[cfg(not(target_arch = "wasm32"))]
let window = winit::window::Window::new(&event_loop).unwrap();
let window = winit::window::Window::new(&event_loop)?;
let physical_size = window.inner_size();
let mut viewport = Viewport::with_physical_size(
@ -61,7 +62,6 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut clipboard = Clipboard::connect(&window);
// Initialize wgpu
#[cfg(target_arch = "wasm32")]
let default_backend = wgpu::Backends::GL;
#[cfg(not(target_arch = "wasm32"))]
@ -95,12 +95,16 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(not(target_arch = "wasm32"))]
let needed_limits = wgpu::Limits::default();
let capabilities = surface.get_capabilities(&adapter);
(
surface
.get_capabilities(&adapter)
capabilities
.formats
.first()
.iter()
.filter(|format| format.describe().srgb)
.copied()
.next()
.or_else(|| capabilities.formats.first().copied())
.expect("Get preferred format"),
adapter
.request_device(

View file

@ -213,12 +213,9 @@ impl Sandbox for App {
column![
scrollable(options).height(Length::Fill),
row![
text_input(
"Add a new option",
&self.input,
Message::InputChanged,
)
.on_submit(Message::AddItem(self.input.clone())),
text_input("Add a new option", &self.input)
.on_input(Message::InputChanged)
.on_submit(Message::AddItem(self.input.clone())),
button(text(format!("Toggle Order ({})", self.order)))
.on_press(Message::ToggleOrder)
]

View file

@ -133,18 +133,16 @@ impl Application for App {
column![
column![
text("Email").size(12),
text_input(
"abc@123.com",
&self.email,
Message::Email
)
.on_submit(Message::Submit)
.padding(5),
text_input("abc@123.com", &self.email,)
.on_input(Message::Email)
.on_submit(Message::Submit)
.padding(5),
]
.spacing(5),
column![
text("Password").size(12),
text_input("", &self.password, Message::Password)
text_input("", &self.password)
.on_input(Message::Password)
.on_submit(Message::Submit)
.password()
.padding(5),

View file

@ -49,13 +49,11 @@ impl Sandbox for QRGenerator {
.size(70)
.style(Color::from([0.5, 0.5, 0.5]));
let input = text_input(
"Type the data of your QR code here...",
&self.data,
Message::DataChanged,
)
.size(30)
.padding(15);
let input =
text_input("Type the data of your QR code here...", &self.data)
.on_input(Message::DataChanged)
.size(30)
.padding(15);
let mut content = column![title, input]
.width(700)

View file

@ -338,22 +338,36 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle {
style.active(&theme::Scrollable::Default)
}
fn hovered(&self, style: &Self::Style) -> Scrollbar {
style.hovered(&theme::Scrollable::Default)
fn hovered(
&self,
style: &Self::Style,
is_mouse_over_scrollbar: bool,
) -> Scrollbar {
style.hovered(&theme::Scrollable::Default, is_mouse_over_scrollbar)
}
fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar {
Scrollbar {
background: style.active(&theme::Scrollable::default()).background,
border_radius: 0.0,
border_width: 0.0,
border_color: Default::default(),
scroller: Scroller {
color: Color::from_rgb8(250, 85, 134),
fn hovered_horizontal(
&self,
style: &Self::Style,
is_mouse_over_scrollbar: bool,
) -> Scrollbar {
if is_mouse_over_scrollbar {
Scrollbar {
background: style
.active(&theme::Scrollable::default())
.background,
border_radius: 0.0,
border_width: 0.0,
border_color: Default::default(),
},
scroller: Scroller {
color: Color::from_rgb8(250, 85, 134),
border_radius: 0.0,
border_width: 0.0,
border_color: Default::default(),
},
}
} else {
self.active(style)
}
}
}

View file

@ -90,13 +90,10 @@ impl Sandbox for Styling {
},
);
let text_input = text_input(
"Type something...",
&self.input_value,
Message::InputChanged,
)
.padding(10)
.size(20);
let text_input = text_input("Type something...", &self.input_value)
.on_input(Message::InputChanged)
.padding(10)
.size(20);
let button = button("Submit")
.padding(10)

View file

@ -119,13 +119,15 @@ impl Application for App {
column![
subtitle(
"Title",
text_input("", &self.editing.title, Message::Title)
text_input("", &self.editing.title)
.on_input(Message::Title)
.on_submit(Message::Add)
.into()
),
subtitle(
"Message",
text_input("", &self.editing.body, Message::Body)
text_input("", &self.editing.body)
.on_input(Message::Body)
.on_submit(Message::Add)
.into()
),

View file

@ -210,15 +210,12 @@ impl Application for Todos {
.style(Color::from([0.5, 0.5, 0.5]))
.horizontal_alignment(alignment::Horizontal::Center);
let input = text_input(
"What needs to be done?",
input_value,
Message::InputChanged,
)
.id(INPUT_ID.clone())
.padding(15)
.size(30)
.on_submit(Message::CreateTask);
let input = text_input("What needs to be done?", input_value)
.id(INPUT_ID.clone())
.on_input(Message::InputChanged)
.on_submit(Message::CreateTask)
.padding(15)
.size(30);
let controls = view_controls(tasks, *filter);
let filtered_tasks =
@ -381,14 +378,12 @@ impl Task {
.into()
}
TaskState::Editing => {
let text_input = text_input(
"Describe your task...",
&self.description,
TaskMessage::DescriptionEdited,
)
.id(Self::text_input_id(i))
.on_submit(TaskMessage::FinishEdition)
.padding(10);
let text_input =
text_input("Describe your task...", &self.description)
.id(Self::text_input_id(i))
.on_input(TaskMessage::DescriptionEdited)
.on_submit(TaskMessage::FinishEdition)
.padding(10);
row![
text_input,

View file

@ -5,7 +5,7 @@ use iced::widget::{
scrollable, slider, text, text_input, toggler, vertical_space,
};
use iced::widget::{Button, Column, Container, Slider};
use iced::{Color, Element, Length, Renderer, Sandbox, Settings};
use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings};
pub fn main() -> iced::Result {
env_logger::init();
@ -127,6 +127,7 @@ impl Steps {
Step::TextInput {
value: String::new(),
is_secure: false,
is_showing_icon: false,
},
Step::Debugger,
Step::End,
@ -171,14 +172,32 @@ impl Steps {
enum Step {
Welcome,
Slider { value: u8 },
RowsAndColumns { layout: Layout, spacing: u16 },
Text { size: u16, color: Color },
Radio { selection: Option<Language> },
Toggler { can_continue: bool },
Image { width: u16 },
Slider {
value: u8,
},
RowsAndColumns {
layout: Layout,
spacing: u16,
},
Text {
size: u16,
color: Color,
},
Radio {
selection: Option<Language>,
},
Toggler {
can_continue: bool,
},
Image {
width: u16,
},
Scrollable,
TextInput { value: String, is_secure: bool },
TextInput {
value: String,
is_secure: bool,
is_showing_icon: bool,
},
Debugger,
End,
}
@ -194,6 +213,7 @@ pub enum StepMessage {
ImageWidthChanged(u16),
InputChanged(String),
ToggleSecureInput(bool),
ToggleTextInputIcon(bool),
DebugToggled(bool),
TogglerChanged(bool),
}
@ -256,6 +276,14 @@ impl<'a> Step {
*can_continue = value;
}
}
StepMessage::ToggleTextInputIcon(toggle) => {
if let Step::TextInput {
is_showing_icon, ..
} = self
{
*is_showing_icon = toggle
}
}
};
}
@ -303,9 +331,11 @@ impl<'a> Step {
Self::rows_and_columns(*layout, *spacing)
}
Step::Scrollable => Self::scrollable(),
Step::TextInput { value, is_secure } => {
Self::text_input(value, *is_secure)
}
Step::TextInput {
value,
is_secure,
is_showing_icon,
} => Self::text_input(value, *is_secure, *is_showing_icon),
Step::Debugger => Self::debugger(debug),
Step::End => Self::end(),
}
@ -530,14 +560,25 @@ impl<'a> Step {
)
}
fn text_input(value: &str, is_secure: bool) -> Column<'a, StepMessage> {
let text_input = text_input(
"Type something to continue...",
value,
StepMessage::InputChanged,
)
.padding(10)
.size(30);
fn text_input(
value: &str,
is_secure: bool,
is_showing_icon: bool,
) -> Column<'a, StepMessage> {
let mut text_input = text_input("Type something to continue...", value)
.on_input(StepMessage::InputChanged)
.padding(10)
.size(30);
if is_showing_icon {
text_input = text_input.icon(text_input::Icon {
font: Font::default(),
code_point: '🚀',
size: Some(28.0),
spacing: 10.0,
side: text_input::Side::Right,
});
}
Self::container("Text input")
.push("Use a text input to ask for different kinds of information.")
@ -551,6 +592,11 @@ impl<'a> Step {
is_secure,
StepMessage::ToggleSecureInput,
))
.push(checkbox(
"Show icon",
is_showing_icon,
StepMessage::ToggleTextInputIcon,
))
.push(
"A text input produces a message every time it changes. It is \
very easy to keep track of its contents:",

View file

@ -13,63 +13,67 @@ use std::fmt;
pub fn connect() -> Subscription<Event> {
struct Connect;
subscription::unfold(
subscription::channel(
std::any::TypeId::of::<Connect>(),
State::Disconnected,
|state| async move {
match state {
State::Disconnected => {
const ECHO_SERVER: &str = "ws://localhost:3030";
100,
|mut output| async move {
let mut state = State::Disconnected;
match async_tungstenite::tokio::connect_async(ECHO_SERVER)
loop {
match &mut state {
State::Disconnected => {
const ECHO_SERVER: &str = "ws://127.0.0.1:3030";
match async_tungstenite::tokio::connect_async(
ECHO_SERVER,
)
.await
{
Ok((websocket, _)) => {
let (sender, receiver) = mpsc::channel(100);
{
Ok((websocket, _)) => {
let (sender, receiver) = mpsc::channel(100);
(
Some(Event::Connected(Connection(sender))),
State::Connected(websocket, receiver),
)
}
Err(_) => {
tokio::time::sleep(
tokio::time::Duration::from_secs(1),
)
.await;
let _ = output
.send(Event::Connected(Connection(sender)))
.await;
(Some(Event::Disconnected), State::Disconnected)
}
}
}
State::Connected(mut websocket, mut input) => {
let mut fused_websocket = websocket.by_ref().fuse();
state = State::Connected(websocket, receiver);
}
Err(_) => {
tokio::time::sleep(
tokio::time::Duration::from_secs(1),
)
.await;
futures::select! {
received = fused_websocket.select_next_some() => {
match received {
Ok(tungstenite::Message::Text(message)) => {
(
Some(Event::MessageReceived(Message::User(message))),
State::Connected(websocket, input)
)
}
Ok(_) => {
(None, State::Connected(websocket, input))
}
Err(_) => {
(Some(Event::Disconnected), State::Disconnected)
}
let _ = output.send(Event::Disconnected).await;
}
}
}
State::Connected(websocket, input) => {
let mut fused_websocket = websocket.by_ref().fuse();
message = input.select_next_some() => {
let result = websocket.send(tungstenite::Message::Text(message.to_string())).await;
futures::select! {
received = fused_websocket.select_next_some() => {
match received {
Ok(tungstenite::Message::Text(message)) => {
let _ = output.send(Event::MessageReceived(Message::User(message))).await;
}
Err(_) => {
let _ = output.send(Event::Disconnected).await;
if result.is_ok() {
(None, State::Connected(websocket, input))
} else {
(Some(Event::Disconnected), State::Disconnected)
state = State::Disconnected;
}
Ok(_) => continue,
}
}
message = input.select_next_some() => {
let result = websocket.send(tungstenite::Message::Text(message.to_string())).await;
if result.is_err() {
let _ = output.send(Event::Disconnected).await;
state = State::Disconnected;
}
}
}
}

View file

@ -125,12 +125,9 @@ impl Application for WebSocket {
};
let new_message_input = {
let mut input = text_input(
"Type a message...",
&self.new_message,
Message::NewMessageChanged,
)
.padding(10);
let mut input = text_input("Type a message...", &self.new_message)
.on_input(Message::NewMessageChanged)
.padding(10);
let mut button = button(
text("Send")