This commit is contained in:
aOK 2025-01-31 17:18:15 +03:00
parent 0e1100f7d1
commit 111b394b4d
34 changed files with 3523 additions and 83 deletions

View file

@ -1,3 +1,7 @@
<<<<<<< HEAD
https://192.168.*.*:8000 {
=======
https://192.168.247.197:8000 {
>>>>>>> 60db8c0 (new code)
reverse_proxy 127.0.0.1:8010
}

View file

@ -8,6 +8,7 @@ sudo cargo makepad wasm run -p aok_manyatta --release --no-default-features --ta
caddy fmt --overwrite /Users/aok/Projects/rustdev/MadeByMakepad/aok_manyatta/Caddyfile
caddy reload --config /Users/aok/Projects/rustdev/MadeByMakepad/aok_manyatta/Caddyfile
caddy run --config /Users/aok/Projects/rustdev/MadeByMakepad/aok_manyatta/Caddyfile
Creating a new repository on the command line
touch README.md

View file

@ -646,3 +646,111 @@ Time Taken: 283.93ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 722.92ms
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 361.37s
Command: cargo pkgid -p aok_manyatta
Time Taken: 464.00ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 42.98s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 17.73s
Command: cargo pkgid -p aok_manyatta
Time Taken: 847.73ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 6.11s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 26.74s
Command: cargo pkgid -p aok_manyatta
Time Taken: 350.69ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 12.57s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 480.54s
Command: cargo pkgid -p aok_manyatta
Time Taken: 267.58ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 5.41s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 334.67s
Command: cargo pkgid -p aok_manyatta
Time Taken: 439.80ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 7.62s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 650.27s
Command: cargo pkgid -p aok_manyatta
Time Taken: 674.92ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 66.65s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 430.74s
Command: cargo pkgid -p aok_manyatta
Time Taken: 329.64ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 3.81s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 146.06s
Command: cargo pkgid -p aok_manyatta
Time Taken: 247.09ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 638.03ms
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 169.72s
Command: cargo pkgid -p aok_manyatta
Time Taken: 226.38ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 539.24ms
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 121.63s
Command: cargo pkgid -p aok_manyatta
Time Taken: 377.13ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 1.65s
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 101.68s
Command: cargo pkgid -p aok_manyatta
Time Taken: 267.93ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 637.07ms
Command: rustup run nightly cargo build --target=wasm32-unknown-unknown -Z build-std=panic_abort,std -p aok_manyatta --release
Time Taken: 111.87s
Command: cargo pkgid -p aok_manyatta
Time Taken: 230.01ms
Command: cargo tree -p aok_manyatta --target=wasm32-unknown-unknown
Time Taken: 531.42ms

4
resources/back.svg Executable file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#000" d="M11.025 13.25a.748.748 0 0 1-1.281.53l-5.25-5.264a.75.75 0 0 1 0-1.06L9.717 2.22a.75.75 0 1 1 1.062 1.06L6.084 7.986l4.722 4.734a.748.748 0 0 1 .219.53z"></path>
</svg>

After

Width:  |  Height:  |  Size: 290 B

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
<path d="M12 17h.01"></path>
</svg>

After

Width:  |  Height:  |  Size: 326 B

1
resources/info.svg Normal file
View file

@ -0,0 +1 @@

View file

@ -27,8 +27,8 @@ live_design!(
flow: Overlay,
home_screen_view = <View> {
visible: true
home_screen = <HomeScreen> {}
// visible: true
home_screen = <HomeScreen> {}
}
}
} // end of body
@ -46,11 +46,27 @@ struct App {
impl MatchEvent for App {
fn handle_startup(&mut self, _cx: &mut Cx) {
// self.stack_navigation(id!(view_stack))
// .show_view(id!(main_content_view));
// let home = std::env::var("HOME")
// .or_else(|_| std::env::var("USERPROFILE"))
// .expect("home not found");
// self.state.load_images(&Path::new(&home).join("Downloads"));
self.state.new();
}
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
// for action in actions {
// // match action.as_widget_action().cast() {
// let widget_uid = self.ui.widget_uid();
// // Navigate to the main content view
// cx.widget_action(
// widget_uid,
// &Scope::default().path,
// StackNavigationAction::NavigateTo(live_id!(main_content_view)),
// );
// // }
// }
}
}
@ -59,10 +75,14 @@ impl AppMain for App {
// self.ui
// .handle_event(cx, event, &mut Scope::with_data(&mut self.state));
// self.match_event(cx, event);
if let Event::WindowGeomChange(window_geom_change_event) = event {
self.state.window_geom = Some(window_geom_change_event.new_geom.clone());
}
// Forward events to the MatchEvent trait implementation.
self.match_event(cx, event);
let scope = &mut Scope::with_data(&mut self.state);
self.ui.handle_event(cx, event, scope);
// self.match_event(cx, event);
}
}
@ -72,6 +92,9 @@ impl LiveRegister for App {
makepad_widgets::live_design(cx);
crate::shared::live_design(cx);
crate::home::live_design(cx);
crate::services::live_design(cx);
crate::howitworks::live_design(cx);
crate::footer::live_design(cx);
}
}

View file

@ -3,12 +3,41 @@ use std::{
path::{Path, PathBuf},
};
use makepad_widgets::event;
#[derive(Default)]
pub struct State {
pub images: Vec<PathBuf>,
pub select_choices_values: Vec<Vec<String>>,
pub window_geom: Option<event::WindowGeom>,
}
impl State {
pub fn new(&mut self) {
self.select_choices_values = vec![
vec![
"STP1-Option 1".to_string(),
"STP1-Option 2".to_string(),
"STP1-Option 3".to_string(),
"STP1-Option 4".to_string(),
"STP1-Option 5".to_string(),
"STP1-Option 6".to_string(),
],
vec![
"STP2-Option 1".to_string(),
"STP2-Option 2".to_string(),
"STP2-Option 3".to_string(),
"STP2-Option 4".to_string(),
],
vec![
"STP3-Option 1".to_string(),
"STP3-Option 2".to_string(),
"STP3-Option 3".to_string(),
"STP3-Option 4".to_string(),
"STP3-Option 5".to_string(),
],
];
}
pub fn load_images(&mut self, path: &Path) {
self.images = fs::read_dir(path)
.expect("unable to read directory")

222
src/footer/footer.rs Normal file
View file

@ -0,0 +1,222 @@
// use event::{FileUploads, SelectedFilesEvent};
use makepad_widgets::*;
use crate::data::state::State;
live_design! {
use link::theme::*;
use link::widgets::*;
use crate::shared::styles::*;
use crate::shared::widgets::*;
BG_COLOR = #3
pub FooterScreen = {{FooterScreen}} {
width: Fill,
height: Fit,
<View> {
width: Fill,
height: Fit,
show_bg: true
draw_bg: {
fn pixel(self) -> vec4 {
return #111827;
}
}
flow: Down
spacing: 20
align: {x: 0.5, y: 0.5},
padding: 60.0
<View>{
width: Fill,
height: Fit,
flow: Right
spacing: 30
padding: {bottom: 40.0}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 20
<Label> {
text: "ProServices"
draw_text: {
text_style: <BOLD_FONT>{font_size: 18},
color: #fff
}
}
model_name = <Label> {
width: Fill
text: "Professional services for all your documentation needs"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 13},
color: (TERTIARY_TEXT_COLOR)
wrap: Word,
}
}
}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 20
<Label> {
text: "Quick Links"
draw_text: {
text_style: <BOLD_FONT>{font_size: 15},
color: #fff
}
}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 10
services = <Label> {
text: "Services"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
howitworks = <Label> {
text: "How it Works"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
contacts = <Label> {
text: "Contacts"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
}
}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 20
<Label> {
text: "Services"
draw_text: {
text_style: <BOLD_FONT>{font_size: 15},
color: #fff
}
}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 10
services = <Label> {
text: "Resume Writing"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
howitworks = <Label> {
text: "Business Plans"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
contacts = <Label> {
text: "Tax Services"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
}
}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 20
<Label> {
text: "Contacts Info"
draw_text: {
text_style: <BOLD_FONT>{font_size: 15},
color: #fff
}
}
<View>{
width: Fill,
height: Fit,
flow: Down
spacing: 10
services = <Label> {
text: "contact@infoservices.com"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
howitworks = <Label> {
text: "+254 713 521197"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
contacts = <Label> {
text: "Nairobi, Kenya"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
}
}
}
<Line> { draw_bg: { color: #x1f2937 }}
<Label> {
padding: {top: 5.0}
text: "© 2025 ProServices. All rights reserved."
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: (TERTIARY_TEXT_COLOR)
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct FooterScreen {
#[deref]
deref: View,
}
impl Widget for FooterScreen {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.deref.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let _state = scope.data.get::<State>().unwrap();
self.deref.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for FooterScreen {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let _state = scope.data.get_mut::<State>().unwrap();
// if self.deref.button(id!(file_button)).clicked(&actions) {
// cx.open_file_dialog("image/*");
// }
}
}

7
src/footer/mod.rs Normal file
View file

@ -0,0 +1,7 @@
use makepad_widgets::Cx;
pub mod footer;
pub fn live_design(cx: &mut Cx) {
footer::live_design(cx);
}

103
src/home/bar.rs Normal file
View file

@ -0,0 +1,103 @@
use makepad_widgets::*;
// use crate::shared::search_bar::SearchBarAction;
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::styles::*;
use crate::shared::helpers::*;
// use crate::shared::search_bar::SearchBar;
use crate::home::rooms_list::RoomsList;
RoomsView = {{RoomsView}} {
show_bg: true,
draw_bg: {
instance bg_color: (COLOR_PRIMARY)
instance border_color: #f2f2f2
instance border_width: 0.003
// Draws a right-side border
fn pixel(self) -> vec4 {
if self.pos.x > 1.0 - self.border_width {
return self.border_color;
} else {
return self.bg_color;
}
}
}
<Label> {
text: "Rooms"
draw_text: {
color: #x0
text_style: <TITLE_TEXT>{}
}
}
}
pub RoomsSideBar = <AdaptiveView> {
Desktop = <RoomsView> {
padding: {top: 20., left: 10., right: 10.}
flow: Down, spacing: 10
width: Fill, height: Fill
},
Mobile = <RoomsView> {
padding: {top: 17., left: 17., right: 17.}
flow: Down, spacing: 7
width: Fill, height: Fill
}
}
}
#[derive(Clone, Debug, DefaultNone)]
pub enum RoomsViewAction {
/// Search for rooms
Search(String),
None,
}
#[derive(Widget, Live, LiveHook)]
pub struct RoomsView {
#[deref]
view: View,
}
impl Widget for RoomsView {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for RoomsView {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
// let widget_uid = self.widget_uid();
// for action in actions {
// match action.as_widget_action().cast() {
// SearchBarAction::Search(keywords) => {
// cx.widget_action(
// widget_uid,
// &scope.path,
// RoomsViewAction::Search(keywords.clone()),
// );
// }
// SearchBarAction::ResetSearch => {
// cx.widget_action(
// widget_uid,
// &scope.path,
// RoomsViewAction::Search("".to_string()),
// );
// }
// _ => {}
// }
// }
}
}

349
src/home/hero.rs Normal file
View file

@ -0,0 +1,349 @@
use makepad_widgets::*;
use crate::{data::state::State, shared::choose::choose::NextBackAction};
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::styles::*;
use crate::shared::widgets::MolyButton;
use crate::shared::widgets::SmallCard;
use crate::shared::choose::choose::Choose;
use crate::shared::choose::final_choose::FinalChoose;
// use crate::home::choose::Choose;
use crate::shared::html_or_plaintext::*;
BG_COLOR = #3
ICON_HELP_CIRCLE = dep("crate://self/resources/help_circle.svg")
pub HeroScreen = {{HeroScreen}} {
width: Fill,
height: Fit,
show_bg: true
flow: Right
// debug: A
align: {x: 0.5, y: 0.0},
draw_bg: {
fn pixel(self) -> vec4 {
return (COLOR_SECONDARY);
// return #000;
}
}
padding: 50.0
// spacing: -20,
// debug: A
<View> {
width: Fill,
height: Fit,
flow: Down
// padding: {top: -50.0}
// spacing: 40,
// align: {x: 0.0, y: 0.5},
// debug: B
<MessageHtml> {
padding: {top: 12, left: 0.}
// spacing: -40.
font_size: 60.
font_color: #x4B5563
draw_normal: {
color: #000
}
// a = {
// padding: {left: 8., right: 8., top: 4., bottom: 5.},
// // draw_text: {
// // text_style: <THEME_FONT_BOLD> {top_drop: 1.2, font_size: 11. },
// // color: #f,
// // color_pressed: #f00,
// // color_hover: #0f0,
// // }
// }
// span = {
// // padding: {left: 8., right: 8., top: 4., bottom: 5.},
// // draw_text: {
// // text_style: <THEME_FONT_BOLD> {top_drop: 1.2, font_size: 12. },
// // color: #x2563EB,
// // // color_pressed: #f00,
// // // color_hover: #0f0,
// // }
// }
body:"<h4>Building, Dreams <span color: x2563EB>Delivering Excellence<span/><h4/>
<h1>
Here
<h1/>
"
}
<View>{
width: Fill,
height: Fit,
flow: Right
// align: {x: 0.5, y: 0.5},
// debug: B
<View>{
width: Fill,
height: Fill,
flow: Down
spacing: 40,
// padding: {bottom: 0.}
align: {x: 0.0, y: 1.0},
// debug: B
<View> {
width: Fill,
height: Fit,
flow: RightWrap
// <Label> {
// width: Fit
// text: "Building, and "
// draw_text: {
// text_style: <TITLE_TEXT>{font_size: 60},
// color: #x4B5563
// wrap: Word,
// }
// }
// <Label> {
// width: Fit
// text: "Delivering Excellence"
// draw_text: {
// text_style: <TITLE_TEXT>{font_size: 60},
// color: #x2563EB
// // wrap: Word,
// }
// }
}
<P> {
width: 650
text: "From architectural masterpieces to regulatory compliance, we're your one-stop solution for all construction and design needs."
draw_text: {
text_style: <TITLE_TEXT>{font_size: 16},
color: #4B5563
wrap: Word,
}
}
<View> {
width: Fit,
height: Fit,
flow: Right
spacing: 20
// debug: B
explore = <MolyButton> {
width: 220,
height: 60,
text: "Explore Services →"
draw_text: {
text_style: <BOLD_FONT>{font_size: 14},
// color: #x2563EB
}
draw_bg: { color: #x2563EB, border_color: #x2563EB }
}
requests = <MolyButton> {
width: 220,
height: 60,
text: "View My Requests >"
draw_text: {
text_style: <BOLD_FONT>{font_size: 14},
// color: #x2563EB
}
draw_bg: { color: #1a365d, border_color: #x2563EB }
}
choose = <MolyButton> {
width: 220,
height: 60,
text: "Help Me Choose ⓘ"
draw_text: {
text_style: <BOLD_FONT>{font_size: 14},
// color: #x2563EB
}
// draw_icon: {
// svg_file: (ICON_HELP_CIRCLE),
// fn get_color(self) -> vec4 {
// return #B42318;
// }
// }
draw_bg: { color: #x2563EB, border_color: #x2563EB }
}
}
<View> {
width: Fit,
height: Fit,
flow: Right
spacing: 20
align: {x: 0.0, y: -0.5},
// debug: B
<SmallCard> {
width: 220,
content = {
title ={
text: "Expert Team"
}
description ={
text: "Professional architects, engineers, and consultants at your service"
}
}
}
<SmallCard> {
width: 220,
content = {
title = {
text: "Fast Turnaround"
}
description ={
text: "Quick response times and efficient project completion"
}
}
}
<SmallCard> {
width: 220,
content = {
title ={
text: "Quality Assured"
}
description ={
text: "Committed to delivering excellence in every project"
}
}
}
}
}
// Choose = <FinalChoose>{
Choose = <Choose>{
choices = {
indicators = {
ind = {
// indicator_titles: [
// "Step Four",
// "Step Five",
// "Step Six"
// ]
}
}
back = {
back_left_button = {
text: "Back"
}
}
steps = {}
next_button = {
next = {
text: "Next"
}
}
}
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct HeroScreen {
#[deref]
deref: View,
}
#[derive(Clone, DefaultNone, Debug)]
pub enum ButtonClickAction {
None,
Click,
}
impl Widget for HeroScreen {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.deref.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
// if let Event::Actions(actions) = event {
// for action in actions {
// if self.deref.button(id!(next)).clicked(&actions) {
// log!("Hero - Next Button Clicked");
// let widget_uid = self.deref.widget_uid();
// // Navigate to the main content view
// cx.widget_action(
// widget_uid,
// &scope.path,
// NextBackAction::NextButton {
// clicked: true,
// page_no: 0, // You can pass the current page number if needed
// },
// );
// log!("Hero - Action dispatched: {:?}", widget_uid);
// self.redraw(cx);
// }
// if self.deref.button(id!(back_left_button)).clicked(&actions) {
// log!("Hero - Next Button Clicked");
// let widget_uid = self.deref.widget_uid();
// // Navigate to the main content view
// cx.widget_action(
// widget_uid,
// &scope.path,
// NextBackAction::BackButton {
// clicked: true,
// page_no: 0, // You can pass the current page number if needed
// },
// );
// log!("Hero - Action dispatched: {:?}", widget_uid);
// self.redraw(cx);
// }
// }
// }
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let _state = scope.data.get::<State>().unwrap();
self.deref.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for HeroScreen {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let _state = scope.data.get_mut::<State>().unwrap();
if self.deref.button(id!(next)).clicked(&actions) {
// log!("Hero - Next Button Clicked");
let widget_uid = self.deref.widget_uid();
// Navigate to the main content view
cx.widget_action(
widget_uid,
&scope.path,
NextBackAction::NextButton {
clicked: true,
page_no: 0, // You can pass the current page number if needed
},
);
// log!("Hero - Action dispatched: {:?}", widget_uid);
self.redraw(cx);
}
if self.deref.button(id!(back_left_button)).clicked(&actions) {
// log!("Hero - Next Button Clicked");
let widget_uid = self.deref.widget_uid();
// Navigate to the main content view
cx.widget_action(
widget_uid,
&scope.path,
NextBackAction::BackButton {
clicked: true,
page_no: 0, // You can pass the current page number if needed
},
);
// log!("Hero - Action dispatched: {:?}", widget_uid);
self.redraw(cx);
}
// if self.deref.button(id!(file_button)).clicked(&actions) {
// cx.open_file_dialog("image/*");
// }
}
}

View file

@ -9,6 +9,7 @@ live_design! {
use crate::shared::styles::*;
use crate::home::main_desktop_ui::MainDesktopUI;
use crate::home::main_web_ui::MainWebUI;
use crate::home::bar::RoomsSideBar;
NavigationWrapper = {{NavigationWrapper}} {
view_stack = <StackNavigation> {}
@ -46,6 +47,8 @@ live_design! {
padding: {top: 40.}
flow: Down
width: Fill, height: Fill
sidebar = <RoomsSideBar> {}
// spaces = <SpacesDock> {}
}
main_content_view = <StackNavigationView> {
@ -136,5 +139,7 @@ impl MatchEvent for NavigationWrapper {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
self.stack_navigation(id!(view_stack))
.handle_stack_view_actions(cx, actions);
// self.stack_navigation(id!(view_stack))
// .show_view(id!(main_content_view));
}
}

View file

@ -5,61 +5,88 @@ use crate::data::state::State;
live_design! {
use link::theme::*;
use link::shaders::*;
// use link::shaders::*;
use link::widgets::*;
use crate::shared::styles::*;
BG_COLOR = #3
// use crate::shared::widgets::*;
use crate::shared::widgets::MolyButton;
use crate::services::services::ServicesScreen;
use crate::howitworks::howitworks::HowItWorksScreen;
use crate::footer::footer::FooterScreen;
use crate::home::hero::HeroScreen;
BG_COLOR = #f9fafb
pub LandingScreen = {{LandingScreen}} {
flow: Down
padding: { top: 30 }
// padding: { top: 30 }
show_bg: true
draw_bg: {
fn pixel(self) -> vec4 {
return (BG_COLOR);
}
}
body = <View> { width: 0, height: 0 }
<View>{
flow: Right
height: Fit
file_button = <Button> {
text: "Select Images"
draw_text: { color: #fff }
}
jump_to_2= <Button> {
text: "Jump to 2"
draw_text:{color:#fff}
}
}
<View>{
flow: Right
// body = <View> { width: 0, height: 0 }
img_list = <PortalList> {
img_btn = <Button> {
width: Fill
<ScrollYView> {
width: Fill,
height: Fill,
flow: Down
// spacing: 10,
// align: {x: 0.5, y: 0.5},
<HeroScreen>{}
<ServicesScreen>{}
<HowItWorksScreen>{}
<FooterScreen>{}
<View>{
flow: Right
height: Fit
file_button = <MolyButton> {
width: 140,
height: 32,
text: "Select Images"
draw_bg: { color: #099250, border_color: #099250 }
}
img_text = <Label> {
width: Fill
draw_text: {
// text_style: <TEXT_BOLD> {},
instance hover: 0.0
instance down: 0.0
fn get_color(self) -> vec4 {
return mix(mix(#xFFFA, #xFFFF, self.hover), #xFFF8, self.down);
}
wrap: Word,
}
text: "Hello World"
jump_to_2= <MolyButton> {
width: 140,
height: 32,
text: "Jump to 2"
draw_bg: { color: #099250, border_color: #099250 }
}
}
img = <Image> {
width: Fill
height: Fill
fit: Smallest
<View>{
flow: Right
img_list = <PortalList> {
img_btn = <Button> {
width: Fill
}
img_text = <Label> {
width: Fill
draw_text: {
// text_style: <TEXT_BOLD> {},
instance hover: 0.0
instance down: 0.0
fn get_color(self) -> vec4 {
return mix(mix(#xFFFA, #xFFFF, self.hover), #xFFF8, self.down);
}
wrap: Word,
}
text: "Hello World"
}
}
img = <Image> {
width: Fill
height: Fill
fit: Smallest
}
}
}
}
}

View file

@ -12,23 +12,11 @@ live_design!(
pub MainDesktopUI = {{MainDesktopUI}} {
align: {x: 0.5, y: 0.5}
// align: {x: 0.5, y: 0.5}
body = <View>{
flow: Down
width: Fit
<Label> {
text: "HEY, MY NAME IS ODEKI KEGODE ANDREW (DESKTOP)"
draw_text: {
text_style: <TEXT_BOLD> {},
instance hover: 0.0
instance down: 0.0
fn get_color(self) -> vec4 {
return mix(mix(#x06060E, #x5C0B11, self.hover), #x76AEAA, self.down);
}
wrap: Word,
}
}
// padding: {top: 20., left: 10., right: 10.}
<LandingScreen>{}
}
}

View file

@ -13,20 +13,6 @@ live_design!(
body = <View>{
flow: Down
width: Fit
<Label> {
text: "HEY, MY NAME IS ODEKI KEGODE ANDREW (MOBILE)"
draw_text: {
text_style: <TEXT_BOLD> {},
instance hover: 0.0
instance down: 0.0
fn get_color(self) -> vec4 {
return mix(mix(#x06060E, #x5C0B11, self.hover), #x76AEAA, self.down);
}
wrap: Word,
}
}
<LandingScreen>{}
}
}

View file

@ -13,20 +13,6 @@ live_design!(
body = <View>{
flow: Down
width: Fit
<Label> {
text: "HEY, MY NAME IS ODEKI KEGODE ANDREW (WEB)"
draw_text: {
text_style: <TEXT_BOLD> {},
instance hover: 0.0
instance down: 0.0
fn get_color(self) -> vec4 {
return mix(mix(#x06060E, #x5C0B11, self.hover), #x76AEAA, self.down);
}
wrap: Word,
}
}
<LandingScreen>{}
}
}

View file

@ -1,5 +1,8 @@
use makepad_widgets::Cx;
pub mod bar;
pub mod choose;
pub mod hero;
pub mod home_screen;
pub mod landing_screen;
pub mod main_desktop_ui;
@ -15,6 +18,9 @@ pub fn live_design(cx: &mut Cx) {
main_mobile_ui::live_design(cx);
main_desktop_ui::live_design(cx);
main_web_ui::live_design(cx);
hero::live_design(cx);
bar::live_design(cx);
choose::live_design(cx);
// ////
// ui::live_design(cx);
// welcome_screen::live_design(cx);

View file

@ -0,0 +1,197 @@
// use event::{FileUploads, SelectedFilesEvent};
use makepad_widgets::*;
use crate::data::state::State;
live_design! {
use link::theme::*;
use link::widgets::*;
use crate::shared::styles::*;
use crate::shared::widgets::MolyButton;
use crate::shared::widgets::Avatar;
BG_COLOR = #f9fafb
pub HowItWorksScreen = {{HowItWorksScreen}} {
padding: { top: 30 }
width: Fill,
height: Fit,
<View> {
width: Fill,
height: Fit,
show_bg: true
draw_bg: {
fn pixel(self) -> vec4 {
return #ffffff;
}
}
flow: Down
spacing: 20
align: {x: 0.5, y: 0.5},
padding: 40.0
<Label> {
text: "How It Works"
draw_text: {
text_style: <BOLD_FONT>{font_size: 30},
color: #111827
}
}
<View>{
width: Fill,
height: Fit,
flow: Right
one = <View>{
width: Fill,
height: Fit,
align: {x: 0.5, y: 0.5},
flow: Down
spacing: 20
avatar = <Avatar> {
width: 70
height: 70
draw_bg: {
radius: 18.0
color: #xdbeafe
}
avatar_label = {
text: "1"
draw_text: {
text_style: <BOLD_FONT>{font_size: 20},
color: #x2563eb
}
}
}
<Label> {
text: "Choose Your Service"
draw_text: {
text_style: <BOLD_FONT>{font_size: 14},
color: #111827
}
}
<Label> {
text: "Select from our wide range of professional services"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: #x4b5563
}
}
}
two = <View>{
width: Fill,
height: Fit,
align: {x: 0.5, y: 0.5},
flow: Down
spacing: 20
avatar = <Avatar> {
width: 70
height: 70
draw_bg: {
radius: 18.0
color: #xdbeafe
}
avatar_label = {
text: "2"
draw_text: {
text_style: <BOLD_FONT>{font_size: 20},
color: #x2563eb
}
}
}
<Label> {
text: "Submit Requirements"
draw_text: {
text_style: <BOLD_FONT>{font_size: 14},
color: #111827
}
}
<Label> {
text: "Provide necessary information and documents"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: #x4b5563
}
}
}
three = <View>{
width: Fill,
height: Fit,
align: {x: 0.5, y: 0.5},
flow: Down
spacing: 20
avatar = <Avatar> {
width: 70
height: 70
draw_bg: {
radius: 18.0
color: #xdbeafe
}
avatar_label = {
text: "3"
draw_text: {
text_style: <BOLD_FONT>{font_size: 20},
color: #x2563eb
}
}
}
<Label> {
text: "Get Results"
draw_text: {
text_style: <BOLD_FONT>{font_size: 14},
color: #111827
}
}
<Label> {
width: Fill
align: {x: 0.5, y: 0.5},
text: "Receive your completed service within the promised timeline"
draw_text: {
text_style: <TITLE_TEXT>{font_size: 12},
color: #x4b5563
wrap: Word,
}
}
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct HowItWorksScreen {
#[deref]
deref: View,
}
impl Widget for HowItWorksScreen {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.deref.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let _state = scope.data.get::<State>().unwrap();
self.deref.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for HowItWorksScreen {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let _state = scope.data.get_mut::<State>().unwrap();
// if self.deref.button(id!(file_button)).clicked(&actions) {
// cx.open_file_dialog("image/*");
// }
}
}

7
src/howitworks/mod.rs Normal file
View file

@ -0,0 +1,7 @@
use makepad_widgets::Cx;
pub mod howitworks;
pub fn live_design(cx: &mut Cx) {
howitworks::live_design(cx);
}

View file

@ -1,4 +1,7 @@
pub mod app;
mod data;
mod home;
mod howitworks;
mod services;
mod shared;
mod footer;

View file

@ -0,0 +1,525 @@
use crate::{
data::{chats::chat::ChatID, store::Store},
shared::modal::ModalWidgetExt,
};
use makepad_widgets::*;
use super::{
chat_history_card_options::ChatHistoryCardOptionsWidgetExt,
// delete_chat_modal::DeleteChatModalAction,
};
live_design! {
use link::theme::*;
use link::widgets::*;
use crate::shared::styles::*;
use crate::shared::widgets::*;
use crate::shared::modal::*;
use crate::chat::shared::Avatar;
use crate::chat::chat_history_card_options::ChatHistoryCardOptions;
// use crate::chat::delete_chat_modal::DeleteChatModal;
ICON_DELETE = dep("crate://self/resources/icons/delete.svg")
EditTextInput = <MolyTextInput> {
width: Fill,
height: Fit,
padding: 0,
empty_message: ""
draw_text: {
text_style:<REGULAR_FONT>{font_size: 10},
word: Wrap,
instance prompt_enabled: 0.0
fn get_color(self) -> vec4 {
return #000;
}
}
}
EditActionButton = <MolyButton> {
width: 56,
height: 31,
spacing: 6,
draw_bg: { color: #099250 }
draw_text: {
text_style: <REGULAR_FONT>{font_size: 9},
fn get_color(self) -> vec4 {
return #fff;
}
}
}
SaveButton = <EditActionButton> {
text: "Save"
}
CancelButton = <EditActionButton> {
draw_bg: { border_color: #D0D5DD, border_width: 1.0, color: #fff }
draw_text: {
text_style: <REGULAR_FONT>{font_size: 9},
fn get_color(self) -> vec4 {
return #000;
}
}
text: "Cancel"
}
ChatHistoryCard = {{ChatHistoryCard}} {
flow: Overlay,
width: Fit,
height: Fit,
content = <RoundedView> {
flow: Down
width: 248
height: Fit
padding: 20
spacing: 12
cursor: Hand
draw_bg: {
color: #fff
border_width: 1
}
<View> {
width: Fill
height: Fit
flow: Right
spacing: 10
padding: { top: 4, bottom: 4 }
margin: 0
title_wrapper = <RoundedView> {
show_bg: true,
draw_bg: {
radius: 12.0,
},
width: Fill,
height: Fit,
flow: Down,
align: {x: 0.5, y: 0.0},
title_input_container = <View> {
visible: false,
width: Fill,
height: Fit,
title_input = <EditTextInput> {}
}
title_label_container = <View> {
visible: false,
width: Fill,
height: Fit,
title_label = <Label> {
width: Fill,
height: Fit,
draw_text:{
text_style: <BOLD_FONT>{font_size: 10},
color: #000,
}
text: ""
}
}
edit_buttons = <View> {
visible: false,
width: Fit,
height: Fit,
margin: {top: 10},
spacing: 6,
save = <SaveButton> {}
cancel = <CancelButton> {}
}
}
// TODO: This is horrible, find a way of getting the position of the button.
chat_options_wrapper = <View> {
width: Fit
height: Fit
padding: 4
chat_options = <MolyButton> {
width: Fit
height: Fit
padding: {top: 0, right: 4, bottom: 6, left: 4}
margin: { top: -4}
draw_bg: {
radius: 5
}
draw_text:{
text_style: <BOLD_FONT>{font_size: 14},
color: #667085,
}
text: "..."
reset_hover_on_click: false
}
}
}
<View> {
width: Fill
height: Fit
align: {y: 1}
avatar = <ChatAgentAvatar> {
width: 30
height: 30
draw_bg: {
radius: 8
}
avatar_label = {
text: ""
}
}
filler = <View> {width: Fill}
date = <Label> {
width: Fit,
height: Fit,
draw_text:{
text_style: <REGULAR_FONT>{font_size: 10},
color: #667085,
}
text: "5:29 PM, 5/12/24"
}
}
}
chat_history_card_options_modal = <Modal> {
align: {x: 0.0, y: 0.0}
bg_view: {
visible: false
}
content: {
chat_history_card_options = <ChatHistoryCardOptions> {}
}
}
delete_chat_modal = <Modal> {
content: {
delete_chat_modal_inner = <DeleteChatModal> {}
}
}
}
}
#[derive(Default, Debug, PartialEq)]
enum TitleState {
OnEdit,
#[default]
Editable,
}
#[derive(Live, LiveHook, Widget)]
pub struct ChatHistoryCard {
#[deref]
view: View,
#[rust]
chat_id: ChatID,
#[rust]
title_edition_state: TitleState,
}
impl Widget for ChatHistoryCard {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let store = scope.data.get_mut::<Store>().unwrap();
let chat = store
.chats
.saved_chats
.iter()
.find(|c| c.borrow().id == self.chat_id)
.unwrap();
if let Some(current_chat_id) = store.chats.get_current_chat_id() {
let content_view = self.view(id!(content));
if current_chat_id == self.chat_id {
let active_border_color = vec3(0.082, 0.522, 0.604);
content_view.apply_over(
cx,
live! {
draw_bg: {border_color: (active_border_color)}
},
);
} else {
let border_color = vec3(0.918, 0.925, 0.941);
content_view.apply_over(
cx,
live! {
draw_bg: {border_color: (border_color)}
},
);
}
}
self.set_title_text(chat.borrow_mut().get_title());
self.update_title_visibility(cx);
let initial_letter = store
.get_last_used_file_initial_letter(self.chat_id)
.unwrap_or('A')
.to_uppercase()
.to_string();
let avatar_label = self.view.label(id!(avatar.avatar_label));
avatar_label.set_text(&initial_letter);
let date_label = self.view.label(id!(date));
// Format date.
// TODO: Feels wrong to asume the id will always be the date, do smth about this.
let datetime =
DateTime::from_timestamp_millis(chat.borrow().id as i64).expect("Invalid timestamp");
let local_datetime: DateTime<Local> = Local.from_utc_datetime(&datetime.naive_utc());
if let Some(formatted_date) = relative_format(local_datetime) {
date_label.set_text(&formatted_date);
}
self.view.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for ChatHistoryCard {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
// let widget_uid = self.widget_uid();
match self.title_edition_state {
TitleState::Editable => self.handle_title_editable_actions(cx, actions, scope),
TitleState::OnEdit => self.handle_title_on_edit_actions(cx, actions, scope),
}
let chat_options_wrapper_rect = self.view(id!(chat_options_wrapper)).area().rect(cx);
if self.button(id!(chat_options)).clicked(actions) {
let wrapper_coords = chat_options_wrapper_rect.pos;
let coords = dvec2(
wrapper_coords.x,
wrapper_coords.y + chat_options_wrapper_rect.size.y,
);
self.chat_history_card_options(id!(chat_history_card_options))
.selected(cx, self.chat_id);
let modal = self.modal(id!(chat_history_card_options_modal));
modal.apply_over(
cx,
live! {
content: { margin: { left: (coords.x), top: (coords.y) } }
},
);
modal.open(cx);
return;
}
if let Some(fe) = self.view(id!(content)).finger_down(actions) {
if fe.tap_count == 1 {
cx.action(ChatHistoryCardAction::ChatSelected);
let store = scope.data.get_mut::<Store>().unwrap();
store.chats.set_current_chat(self.chat_id);
self.redraw(cx);
}
}
for action in actions {
if matches!(
action.cast(),
DeleteChatModalAction::Cancelled
| DeleteChatModalAction::CloseButtonClicked
| DeleteChatModalAction::ChatDeleted
) {
self.modal(id!(delete_chat_modal)).close(cx);
}
}
}
}
impl ChatHistoryCard {
pub fn set_chat_id(&mut self, id: ChatID) {
if id != self.chat_id {
self.chat_id = id;
self.title_edition_state = TitleState::Editable;
}
}
fn set_title_text(&mut self, text: &str) {
self.view.label(id!(title_label)).set_text(text.trim());
if let TitleState::Editable = self.title_edition_state {
self.view.text_input(id!(title_input)).set_text(text.trim());
}
}
fn update_title_visibility(&mut self, cx: &mut Cx) {
let on_edit = matches!(self.title_edition_state, TitleState::OnEdit);
self.view(id!(edit_buttons)).set_visible(on_edit);
self.view(id!(title_input_container)).set_visible(on_edit);
self.button(id!(chat_options)).set_visible(!on_edit);
let editable = matches!(self.title_edition_state, TitleState::Editable);
self.view(id!(title_label_container)).set_visible(editable);
self.redraw(cx);
}
fn transition_title_state(&mut self, cx: &mut Cx) {
self.title_edition_state = match self.title_edition_state {
TitleState::OnEdit => TitleState::Editable,
TitleState::Editable => TitleState::OnEdit,
};
self.update_title_visibility(cx);
}
pub fn handle_title_editable_actions(
&mut self,
cx: &mut Cx,
actions: &Actions,
_scope: &mut Scope,
) {
for action in actions {
match action.cast() {
ChatHistoryCardAction::MenuClosed(chat_id) => {
if chat_id == self.chat_id {
self.button(id!(chat_options)).reset_hover(cx);
self.modal(id!(chat_history_card_options_modal)).close(cx);
}
}
ChatHistoryCardAction::ActivateTitleEdition(chat_id) => {
if chat_id == self.chat_id {
self.transition_title_state(cx);
}
}
ChatHistoryCardAction::DeleteChatOptionSelected(chat_id) => {
if chat_id == self.chat_id {
let mut delete_modal_inner =
self.delete_chat_modal(id!(delete_chat_modal_inner));
delete_modal_inner.set_chat_id(self.chat_id);
self.modal(id!(delete_chat_modal)).open(cx);
}
}
_ => {}
}
// If the modal is dissmised (such as, clicking outside) we need to reset the hover state
// of the open chat options button.
if self
.modal(id!(chat_history_card_options_modal))
.dismissed(actions)
{
self.button(id!(chat_options)).reset_hover(cx);
}
}
}
fn handle_title_on_edit_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let store = scope.data.get_mut::<Store>().unwrap();
if self.button(id!(save)).clicked(actions) {
let updated_title = self.text_input(id!(title_input)).text();
let chat = store
.chats
.saved_chats
.iter()
.find(|c| c.borrow().id == self.chat_id)
.unwrap();
if !updated_title.trim().is_empty() && chat.borrow().get_title() != updated_title {
chat.borrow_mut().set_title(updated_title.clone());
chat.borrow().save();
}
self.transition_title_state(cx)
}
if let Some(val) = self.text_input(id!(title_input)).returned(actions) {
let chat = store
.chats
.saved_chats
.iter()
.find(|c| c.borrow().id == self.chat_id)
.unwrap();
if !val.trim().is_empty() && chat.borrow().get_title() != val {
chat.borrow_mut().set_title(val.clone());
chat.borrow().save();
}
self.transition_title_state(cx)
}
if self.button(id!(cancel)).clicked(actions) {
self.transition_title_state(cx)
}
}
}
impl ChatHistoryCardRef {
pub fn set_chat_id(&mut self, id: ChatID) -> Result<(), &'static str> {
let Some(mut inner) = self.borrow_mut() else {
return Err("Widget not found in the document");
};
inner.set_chat_id(id);
Ok(())
}
}
#[derive(Clone, DefaultNone, Eq, Hash, PartialEq, Debug)]
pub enum ChatHistoryCardAction {
None,
ChatSelected,
ActivateTitleEdition(ChatID),
MenuClosed(ChatID),
DeleteChatOptionSelected(ChatID),
}
fn relative_format(datetime: DateTime<Local>) -> Option<String> {
// Calculate the time difference between now and the given timestamp
let now = Local::now();
let duration = now - datetime;
// Handle different time ranges and format accordingly
if duration < Duration::seconds(60) {
Some("Now".to_string())
} else if duration < Duration::minutes(60) {
let minutes_text = if duration.num_minutes() == 1 { "min" } else { "mins" };
Some(format!("{} {} ago", duration.num_minutes(), minutes_text))
} else if duration < Duration::hours(24) && now.date_naive() == datetime.date_naive() {
Some(format!("{}", datetime.format("%H:%M"))) // "HH:MM" format for today
} else if duration < Duration::hours(48) {
if let Some(yesterday) = now.date_naive().succ_opt() {
if yesterday == datetime.date_naive() {
return Some(format!("Yesterday at {}", datetime.format("%H:%M")));
}
}
Some(format!("{}", datetime.format("%A"))) // Fallback to day of the week if not yesterday
} else if duration < Duration::weeks(1) {
Some(format!("{}", datetime.format("%A"))) // Day of the week (e.g., "Tuesday")
} else {
Some(format!("{}", datetime.format("%F"))) // "YYYY-MM-DD" format for older messages
}
}

7
src/services/mod.rs Normal file
View file

@ -0,0 +1,7 @@
use makepad_widgets::Cx;
pub mod services;
pub fn live_design(cx: &mut Cx) {
services::live_design(cx);
}

103
src/services/services.rs Normal file
View file

@ -0,0 +1,103 @@
// use event::{FileUploads, SelectedFilesEvent};
use makepad_widgets::*;
use crate::data::state::State;
live_design! {
use link::theme::*;
use link::widgets::*;
use crate::shared::styles::*;
use crate::shared::widgets::MolyButton;
BG_COLOR = #3
pub ServicesScreen = {{ServicesScreen}} {
width: Fill,
height: Fit,
<View> {
width: Fill,
height: Fit,
flow: Down
align: {x: 0.5, y: 0.5},
<Label> {
text: "Our Services"
draw_text: {
text_style: <BOLD_FONT>{font_size: 30},
color: #111827
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct ServicesScreen {
#[deref]
deref: View,
}
impl Widget for ServicesScreen {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.deref.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let _state = scope.data.get::<State>().unwrap();
self.deref.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for ServicesScreen {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let _state = scope.data.get_mut::<State>().unwrap();
// if self.deref.button(id!(file_button)).clicked(&actions) {
// cx.open_file_dialog("image/*");
// }
}
// fn handle_file_uploads(&mut self, cx: &mut Cx, e: &SelectedFilesEvent, scope: &mut Scope) {
// for event in e {
// match &event.file {
// FileUploads::SelectedFile(file) => {
// // if let Some(data) = file.get_file_upload_data() {
// // log!("File size: {:?} bytes", data);
// // }
// log!("File size: {:?} bytes", file.get_file_name());
// // alright we got an image back
// // match event.request_id {
// // live_id!(llm)=>if let Some(res) = file.get.get_string_body() {
// // // lets parse it as json
// // // if let Ok(val) = JsonValue::deserialize_json(&res){
// // // if let Some(val) = val.key("content"){
// // // if let Some(val) = val.string(){
// // // if let Some((LLMMsg::Progress,_)) = self.llm_chat.last(){
// // // self.llm_chat.pop();
// // // }
// // // let val = val.strip_prefix("assistant").unwrap_or(val);
// // // let val = val.to_string().replace("\"","");
// // // let val = val.trim();
// // // self.ui.text_input(id!(prompt_input)).set_text(cx, &val);
// // // self.llm_chat.push((LLMMsg::AI,val.into()));
// // // self.ui.widget(id!(llm_chat)).redraw(cx);
// // // }
// // // }
// // // else{
// // // log!("{}", res);
// // // }
// // // }
// // // else{
// // // log!("{}", res);
// // // }
// // }
// // }
// }
// _ => (),
// }
// }
// }
}

226
src/shared/choose/choose.rs Normal file
View file

@ -0,0 +1,226 @@
use makepad_widgets::*;
use crate::data::state::State;
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::choose::steps::Steps;
use crate::shared::choose::selectoptions::ButChoices;
use crate::shared::choose::steps::StepsScreen;
use crate::shared::choose::indicator::Indicators;
use crate::shared::widgets::MolyButton;
use crate::shared::styles::*;
pub Choose = {{Choose}}{
// debug: A
width: Fit,
// height: Fill,
height: 488,
// height: Fit,
padding: {left: 50.}, spacing: 20
// align: {x: 0.5, y: 0.5}
choices = <View>{
// debug: A
flow: Down,
width: Fit,
// height: Fill,
// spacing: 20
align: {y: 0.5}
indicators = <View>{
// debug: A
height: Fit,
width: Fit,
margin: {bottom: 20.0}
ind = <Indicators>{
indicator_titles: [
"Step One",
"Step Two",
"Step Three"
]
}
}
back = <View> {
// debug: A
width: Fit, height: Fit,
back_left_button = <Button> {
width: Fit, height: Fit,
icon_walk: {width: 10, height: Fit}
text: "Back"
draw_text: {
text_style: <REGULAR_TEXT>{font_size: 10.},
fn get_color(self) -> vec4 {
return #016def
return mix(mix(#000, #000, self.hover), #000, self.pressed)
}
}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
svg_file: dep("crate://self/resources/back.svg"),
// color: (THEME_COLOR_TEXT_DEFAULT);
color: #016def;
brightness: 0.8;
}
}
}
steps = <Steps>{
page_template: <StepsScreen> {}
selects_template: <ButChoices> {}
page_titles: ["What stage is your project in?", "What type of property is it?", "What's your primary concern?" ]
choices_values: [
["STP1-Option 1"],
["STP2-Option 1", "STP2-Option 2", "STP2-Option 3"],
["STP3-Option 1", "STP3-Option 2"]
]
}
next_button = <View> {
// debug: A
height: Fit
align: {x: 0.5, y: 0.5}
next = <MolyButton> {
// debug: A
width: 300, height: 50,
// margin: 0, padding: 0
text: "Next"
draw_text: {
fn get_color(self) -> vec4 {
// return #016def // 0160C0
return mix(mix(#f0, #016def, self.hover), #f0, self.pressed)
}
}
draw_bg: {
border_radius: 2.
fn pixel(self) -> vec4 {
let border_color = #0157c0;
// let border_color = #016def; //#0157c0
let border_width = 0.5;
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let body = mix(mix(#0157c0, #f, self.hover), #0157c0, self.pressed);
sdf.box(
1.,
1.,
self.rect_size.x - 2.0,
self.rect_size.y - 2.0,
self.border_radius
)
sdf.fill_keep(body)
sdf.stroke(
border_color,
border_width
)
return sdf.result
}
}
}
}
}
}
}
#[derive(Live, Widget)]
pub struct Choose {
#[deref]
view: View,
#[animator]
animator: Animator,
#[rust]
screen_width: f64,
}
#[derive(Clone, Debug)]
pub enum AllPages {
Pages {
content: Vec<Vec<String>>,
page_no: u8,
},
}
#[derive(Clone, DefaultNone, Debug)]
pub enum NextBackAction {
/// The user clicked the "react" button on a message
/// and wants to send the given `reaction` to that message.
NextButton {
clicked: bool,
page_no: u8,
},
BackButton {
clicked: bool,
page_no: u8,
},
SetIndicator {
set: bool,
page_no: u8,
},
None,
}
impl LiveHook for Choose {
fn after_new_from_doc(&mut self, cx: &mut Cx) {}
}
impl Widget for Choose {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk_all(cx, scope, walk);
// log!("Choose: draw_walk");
DrawStep::done()
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
}
}
impl WidgetMatchEvent for Choose {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
if self.button(id!(next)).clicked(&actions) {
// Emit an action to increment the page number
log!("Choose - Next Button Clicked");
cx.action(NextBackAction::NextButton {
clicked: true,
page_no: 0, // You can pass the current page number if needed
});
}
for action in actions {
if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
self.screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
log!("SCREEN_WIDTH ===> {}", self.screen_width);
}
}
}
}
impl Choose {
fn back_left_view(&mut self, cx: &mut Cx) {
// // Fire the "show" animation when the "restart" animation is done
// if self.animator.animator_in_state(cx, id!(choice.restart)) {
// self.animator_play(cx, id!(choice.show));
// }
self.animator_play(cx, id!(choice.show));
// cx.widget_action(
// self.widget_uid(),
// &HeapLiveIdPath::default(),
// StackNavigationTransitionAction::HideBegin,
// );
}
fn next_page_view(&mut self, cx: &mut Cx) {
// // Fire the "show" animation when the "restart" animation is done
// if self.animator.animator_in_state(cx, id!(choice.restart)) {
// self.animator_play(cx, id!(choice.show));
// }
self.animator_play(cx, id!(choice.show));
// cx.widget_action(
// self.widget_uid(),
// &HeapLiveIdPath::default(),
// StackNavigationTransitionAction::HideBegin,
// );
}
}

View file

@ -0,0 +1,267 @@
use makepad_widgets::*;
use crate::data::state::State;
use super::choose::NextBackAction;
const SELECTED: bool = false;
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::widgets::MolyButton;
use crate::shared::styles::*;
IndicatorCheckBox = <CheckBox> {
width: Fill,
height: 35,
margin: {left: 1},
label_walk: {margin: {top: 15, bottom: 10}}
draw_check: {
uniform size: 3.5;
instance open: 0.0
uniform length: 3.0
uniform width: 1.0
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
match self.check_type {
CheckType::Check => {
let sz = self.size;
let left = sz + 1.;
let up = sz + 7;
let c = self.rect_size * vec2(0.5, 0.5);
sdf.box(
left,
c.y - up,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(#232323);
sdf.stroke(#000, 0.5 + 0.5 * self.dpi_dilate);
let isz = sz * 0.5;
let ileft = isz + 3;
let iup = sz + 7;
sdf.box(
ileft,
c.y - iup,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(mix(#000, #016def, self.selected));
sdf.stroke(mix(
#000, #016def,
self.selected), 1.25
);
}
}
return sdf.result
}
}
draw_text: {text_style: <THEME_FONT_LABEL> {}}
}
pub Indicators = {{Indicators}} {
width: Fit,
height: Fit,
flow: Right,
// flow: Down,
spacing: 5,
// align: {x: 0.5, y: 0.5},
indicator_titles: []
template: <IndicatorCheckBox> {
width: 100, height: 20,
margin: 0, padding: 0
// flow: Right,
// flow: Down,
align: {x: 0.5, y: 0.5},
text: "Here",
draw_text: {
fn get_color(self) -> vec4 {
return #f60;
}
}
draw_check: {
check_type: Check,
}
animator: {
selected = {
default: on
}
}
}
}
}
#[derive(Live, Widget)]
pub struct Indicators {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
indicator_titles: Vec<String>,
#[live]
template: Option<LivePtr>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
#[rust(0)]
page_no: u8,
}
impl LiveHook for Indicators {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// fn after_new_from_doc(&mut self, cx: &mut Cx) {
// let tags = ["test1", "test2", "test3"];
// log!("Indicators: after_apply");
self.items.clear();
self.page_no = 0; // Assign initial page
if self.page_no < self.indicator_titles.len() as u8 {
for (i, title_text) in self.indicator_titles.iter().enumerate() {
let item_id = LiveId::from_str(&format!("items{}", i));
let item_widget = WidgetRef::new_from_ptr(cx, self.template);
item_widget.apply_over(cx, live! {text: (title_text)});
let indicator = item_widget.check_box(&[item_id]);
if usize::from(self.page_no) == i {
if indicator.selected(cx) != SELECTED {
indicator.set_selected(cx, SELECTED);
}
}
self.items.insert(item_id, item_widget);
}
}
}
}
impl WidgetMatchEvent for Indicators {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
// if (self.page_no as usize) < arr.len() {
// // Iterate over the selected page's choices
let mut arr: Vec<WidgetRef> = Vec::new();
for (_idx, inner_vec) in self.items.iter_mut() {
// Append the inner vector to the new variable
arr.push(inner_vec.clone());
}
if (self.page_no as usize) < arr.len() {
for (inneridx, indicator_arr) in arr.iter().enumerate() {
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("items{}", index));
let item = self.items.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.template)
});
let indicator = item.check_box(&[widget_id]);
// if usize::from(self.page_no) == inneridx {
if indicator.selected(cx) != SELECTED {
indicator.set_selected(cx, SELECTED);
}
// }
// item.check_box(&[widget_id]).set_selected(cx, SELECTED);
// let _ = item.draw_all(cx, scope);
}
}
// } else {
// log!("Invalid page_no: {}", self.page_no);
// }
for action in actions {
if let NextBackAction::NextButton { clicked, page_no } =
action.as_widget_action().cast()
{
if clicked {
self.increment_page_no(cx); // Increment the page number
}
}
if let NextBackAction::BackButton { clicked, page_no } =
action.as_widget_action().cast()
{
if clicked {
self.decrement_page_no(cx); // Increment the page number
}
}
}
}
}
impl Widget for Indicators {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, item) in self.items.iter_mut() {
item.handle_event(cx, event, scope);
}
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
// let state = scope.data.get_mut::<State>().unwrap();
// state.select_choices_values.len();
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl Indicators {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
cx.begin_turtle(walk, Layout::default());
let mut arr: Vec<WidgetRef> = Vec::new();
for (_idx, inner_vec) in self.items.iter_mut() {
// Append the inner vector to the new variable
arr.push(inner_vec.clone());
}
if (self.page_no as usize) < arr.len() {
for (inneridx, indicator_arr) in arr.iter().enumerate() {
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("items{}", index));
let item = self.items.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.template)
});
let indicator = item.check_box(&[widget_id]);
// if usize::from(self.page_no) == inneridx {
if indicator.selected(cx) != SELECTED {
indicator.set_selected(cx, SELECTED);
}
// }
let _ = item.draw_all(cx, scope);
}
}
cx.end_turtle_with_area(&mut self.area);
// cx.end_turtle_with_area(&mut self.custom_button.area());
}
pub fn increment_page_no(&mut self, cx: &mut Cx) {
self.page_no = (self.page_no + 1).min(2); // Ensure page_no doesn't exceed max value
log!("SelectOption - Navigated to page: {}", self.page_no);
self.update_view(cx);
}
pub fn decrement_page_no(&mut self, cx: &mut Cx) {
if self.page_no > 0 {
self.page_no -= 1;
log!("SelectOption - Navigated to page: {}", self.page_no);
self.update_view(cx);
}
}
fn update_view(&mut self, cx: &mut Cx) {
match self.page_no {
0 => log!("SelectOption - Showing Step One"),
1 => log!("SelectOption - Showing Step Two"),
2 => log!("SelectOption - Showing Step Three"),
_ => log!("SelectOption - Unknown Page"),
}
}
}

15
src/shared/choose/mod.rs Normal file
View file

@ -0,0 +1,15 @@
use makepad_widgets::Cx;
pub mod choose;
// pub mod final_choose;
pub mod indicator;
pub mod selectoptions;
pub mod steps;
pub fn live_design(cx: &mut Cx) {
selectoptions::live_design(cx);
indicator::live_design(cx);
choose::live_design(cx);
steps::live_design(cx);
// final_choose::live_design(cx);
}

View file

@ -0,0 +1,471 @@
use makepad_widgets::*;
use crate::data::state::State;
use super::choose::NextBackAction;
const CHOICE_MAX_OFFSET: f64 = 700.0;
const SELECT_MAX_OFFSET: f64 = 500.0;
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::widgets::MolyButton;
use crate::shared::styles::*;
COLOR_DOWN_6 = #x000000CC
SIDEBAR_FONT_COLOR = #344054
SIDEBAR_FONT_COLOR_HOVER = #344054
SIDEBAR_FONT_COLOR_SELECTED = #127487
SIDEBAR_BG_COLOR = #f
SIDEBAR_BG_COLOR_HOVER = #E2F1F199
SIDEBAR_BG_COLOR_SELECTED = #E2F1F199
pub ButChoices = <ButtonGroup>{
// debug: A
flow: Down,
show_bg: false
draw_bg: {
color: #000
}
align: {x: 0.5, y: 0.5}
but = <View> {
// debug: A
width: 300, height: 45,
butchoice = <RadioButtonTab> {
width: Fill,
height: 70,
align: {x: 0.5, y: 0.5}
text: "Option 1"
label_walk: { width: Fit, height: 34.0 }
icon_walk: {width: 80, height: 80}
draw_radio: {
radio_type: Tab,
instance border_width: 0.0
instance border_color: #344054
instance inset: vec4(0.0, 0.0, 0.0, 0.0)
instance radius: 2.5
fn get_color(self) -> vec4 {
return mix(
mix(
(SIDEBAR_BG_COLOR),
(SIDEBAR_BG_COLOR_HOVER),
self.hover
),
(SIDEBAR_BG_COLOR_SELECTED),
self.selected
)
}
fn get_border_color(self) -> vec4 {
return self.border_color
}
fn pixel(self) -> vec4 {
let border_color = #0157c0;
let border_width = 0.0;
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
sdf.box(
self.inset.x + self.border_width,
self.inset.y + self.border_width,
self.rect_size.x - (self.inset.x + self.inset.z + self.border_width * 2.0),
self.rect_size.y - (self.inset.y + self.inset.w + self.border_width * 2.0),
max(1.0, self.radius)
)
sdf.fill_keep(self.get_color())
if self.border_width > 0.0 {
sdf.stroke(self.get_border_color(), self.border_width)
}
sdf.stroke(
border_color,
border_width
)
return sdf.result;
}
}
draw_text: {
color_unselected: (SIDEBAR_FONT_COLOR)
color_unselected_hover: (SIDEBAR_FONT_COLOR_HOVER)
color_selected: (SIDEBAR_FONT_COLOR_SELECTED)
fn get_color(self) -> vec4 {
return mix(
mix(
self.color_unselected,
self.color_unselected_hover,
self.hover
),
self.color_selected,
self.selected
)
}
}
}
}
}
pub SelectOptions = {{SelectOptions}} {
// page_titles: []
width: Fit, height: Fit,
// indicator_titles: []
flow: Down,
spacing: 10,
align: {x: 0.5, y: 0.5},
select_choices_values: []
selects_template: <ButChoices> {
flow: Down,
}
choice_page_offset: 0.0
animator: {
selectopt = {
default: restart,
restart = {
ease: ExpDecay {d1: 0.80, d2: 0.97}
from: {all: Snap}
apply: {choice_page_offset: 500.0}
}
show = {
redraw: true,
ease: ExpDecay {d1: 0.80, d2: 0.97}
// from: {all: Forward {duration: 0}}
from: {all: Snap}
apply: {choice_page_offset: 0.0}
}
}
}
}
}
#[derive(Live, Widget)]
pub struct SelectOptions {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
page_titles: Vec<String>,
#[live]
select_choices_values: Vec<Vec<String>>,
#[live]
selects_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust(0)]
page_no: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
choice_page_offset: f64,
#[animator]
animator: Animator,
/// The persistent UI-relevant states for the selecte options that this widget is currently displaying.
#[rust]
select_options_state: Option<SelectOptionUiState>,
}
struct SelectOptionUiState {
prev_first_index: Option<usize>,
}
impl LiveHook for SelectOptions {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// fn after_new_from_doc(&mut self, cx: &mut Cx) {
// log!(
// "============SelectOptions::after_new_from_doc============, {:?}",
// self.page_no
// );
// fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// log!("SelectOptions: after_apply");
self.select_choices_values = vec![
vec![
"Planning Phase".to_string(),
"Ready to Start".to_string(),
"In Progress".to_string(),
"Needs Approval".to_string(),
],
vec![
"Residential".to_string(),
"Commercial".to_string(),
"Industrial".to_string(),
"Mixed Use".to_string(),
],
vec![
"Design".to_string(),
"Construction".to_string(),
"Approvals".to_string(),
"Cost Management".to_string(),
],
];
self.page_no = 0; // Assign initial page
if self.page_no < self.select_choices_values.len() as u8 {
for (idx, title_text) in self.select_choices_values[self.page_no as usize]
.iter()
.enumerate()
{
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
page.radio_button(id!(butchoice))
.set_text(cx, &format!("{}", title_text.as_str()));
}
} else {
log!("Warning: page_no is out of bounds");
}
}
}
impl Widget for SelectOptions {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, page) in self.pages.iter_mut() {
page.handle_event(cx, event, scope);
}
// if let Event::Actions(actions) = event {
// for action in actions {
// if let NextBackAction::NextButton { clicked, page_no } =
// action.as_widget_action().cast()
// {
// log!(
// "SelectOption - handle_event - Navigated to page: {}",
// page_no
// );
// if clicked {
// self.increment_page_no(cx); // Increment the page number
// // self.redraw(cx);
// }
// }
// if let NextBackAction::BackButton { clicked, page_no } =
// action.as_widget_action().cast()
// {
// log!(
// "SelectOption - handle_event - Navigated to page: {}",
// page_no
// );
// if clicked {
// self.decrement_page_no(cx); // Increment the page number
// // let walk = self.walk(cx);
// // self.draw_walkd(cx, scope, walk);
// // self.redraw(cx);
// }
// }
// }
// }
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl WidgetMatchEvent for SelectOptions {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
let mut arr: Vec<Vec<String>> = self.select_choices_values.clone();
let mut widget_id_vec: Vec<LiveId> = Vec::new();
let mut pagec: Vec<WidgetRef> = Vec::new();
if (self.page_no as usize) < arr.len() {
// Iterate over the selected page's choices
for (inneridx, choices_arr) in arr[self.page_no as usize].iter().enumerate() {
let offset: f64 = self.choice_page_offset - SELECT_MAX_OFFSET;
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
widget_id_vec.push(widget_id.clone());
pagec.push(page.clone());
// Handle any actions with the `page` widget if needed
page.radio_button(id!(butchoice))
.set_text(cx, choices_arr.as_str());
}
} else {
log!("Invalid page_no: {}", self.page_no);
}
// page.radio_button_set(&[&[widget_id]]).selected_to_visible(
// cx,
// &pagec,
// actions,
// &[&[widget_id]],
// );
for action in actions {
if let NextBackAction::NextButton { clicked, page_no } =
action.as_widget_action().cast()
{
if clicked {
self.increment_page_no(cx); // Increment the page number
}
}
if let NextBackAction::BackButton { clicked, page_no } =
action.as_widget_action().cast()
{
if clicked {
self.decrement_page_no(cx); // Increment the page number
}
}
}
}
}
impl SelectOptions {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
cx.begin_turtle(walk, self.layout);
// Ensure page_no is within the bounds of select_choices_values
if (self.page_no as usize) < self.select_choices_values.len() {
let offset: f64 = self.choice_page_offset - SELECT_MAX_OFFSET;
// Iterate over the current page's choices
for (inneridx, choices_arr) in self.select_choices_values[self.page_no as usize]
.iter()
.enumerate()
{
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
page.radio_button(id!(butchoice))
.set_text(cx, &format!("{}", choices_arr.as_str()));
// Draw each widget with margin adjustment
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
} else {
log!("Invalid page_no: {}", self.page_no);
}
cx.end_turtle_with_area(&mut self.area);
}
pub fn increment_page_no(&mut self, cx: &mut Cx) {
self.page_no = (self.page_no + 1).min(2); // Ensure page_no doesn't exceed max value
log!("SelectOption - Navigated to page: {}", self.page_no);
self.update_view(cx);
}
pub fn decrement_page_no(&mut self, cx: &mut Cx) {
if self.page_no > 0 {
self.page_no -= 1;
log!("SelectOption - Navigated to page: {}", self.page_no);
self.update_view(cx);
}
}
fn update_view(&mut self, cx: &mut Cx) {
match self.page_no {
0 => log!("SelectOption - Showing Step One"),
1 => log!("SelectOption - Showing Step Two"),
2 => log!("SelectOption - Showing Step Three"),
_ => log!("SelectOption - Unknown Page"),
}
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
// let _ = page.draw_all(cx, scope);
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.select_choices_values[0].len() as u8;
log!(
"SelectOptions::update_current_page: {:?}",
&self.current_page
);
}
}
impl SelectOptions {
/// Handles any [`NextBackAction`]s received by this SelectOptions.
fn handle_message_actions(
&mut self,
cx: &mut Cx,
actions: &ActionsBuf,
// portal_list: &PortalListRef,
// loading_pane: &LoadingPaneRef,
) {
let room_screen_widget_uid = self.widget_uid();
// for action in actions {
// match action
// .as_widget_action()
// .widget_uid_eq(room_screen_widget_uid)
// .cast()
// {
// NextBackAction::NextButton { clicked, page_no } => {
// // let Some(tl) = self.tl_state.as_mut() else {
// // return;
// // };
// // let mut success = false;
// // if let Some(timeline_item) = tl.items.get(details.item_id) {
// // if let Some(event_tl_item) = timeline_item.as_event() {
// // if event_tl_item.event_id() == details.event_id.as_deref() {
// // let timeline_event_id = event_tl_item.identifier();
// // // submit_async_request(MatrixRequest::ToggleReaction {
// // // room_id: tl.room_id.clone(),
// // // timeline_event_id,
// // // reaction,
// // // });
// // success = true;
// // }
// // }
// // }
// if !success {
// // enqueue_popup_notification(
// // "Couldn't find message in timeline to react to.".to_string(),
// // );
// // error!("MessageAction::React: couldn't find event [{}] {:?} to react to in room {}",
// // details.item_id,
// // details.event_id.as_deref(),
// // tl.room_id,
// // );
// }
// }
// NextBackAction::None => {}
// }
// }
}
}

292
src/shared/choose/steps.rs Normal file
View file

@ -0,0 +1,292 @@
use makepad_widgets::*;
use crate::data::state::State;
use super::choose::NextBackAction;
const CHOICE_MAX_OFFSET: f64 = 700.0;
const SELECT_MAX_OFFSET: f64 = 0.0;
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::widgets::MolyButton;
use crate::shared::choose::selectoptions::ButChoices;
use crate::shared::choose::selectoptions::SelectOptions;
use crate::shared::styles::*;
COLOR_DOWN_6 = #x000000CC
SIDEBAR_FONT_COLOR = #344054
SIDEBAR_FONT_COLOR_HOVER = #344054
SIDEBAR_FONT_COLOR_SELECTED = #127487
SIDEBAR_BG_COLOR = #f
SIDEBAR_BG_COLOR_HOVER = #E2F1F199
SIDEBAR_BG_COLOR_SELECTED = #E2F1F199
pub StepsScreen = <View> {
height: Fit,
flow: Down,
align: {y: 0.}
spacing: 20.,
title = <View> {
padding: {left: 0.0},
height: Fit
spacing: 0,
step_title = <Label> {
width: Fill
draw_text: {
text_style: <TEXT_BOLD>{font_size: 20.},
color: (COLOR_DOWN_6)
wrap: Word,
}
text: "Step One"
}
}
avail_options = <View> {
// debug: A
align: {x: 0.5, y: 0.5}
height: Fit
<SelectOptions> {
select_choices_values: []
}
}
}
pub Steps = {{Steps}} {
// debug: A
page_titles: []
choices_values: []
page_template: <StepsScreen> {}
selects_template: <ButChoices> {}
step_page_offset: 0.0
animator: {
step_choice = {
default: restart,
restart = {
from: {all: Snap}
apply: {step_page_offset: 400.0}
}
show = {
redraw: true,
from: {all: Forward {duration: 0.5}}
apply: {step_page_offset: 0.0}
}
}
}
}
}
#[derive(Live, Widget)]
pub struct Steps {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[live]
page_titles: Vec<String>,
#[live]
choices_values: Vec<Vec<String>>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
#[live]
pub page_template: Option<LivePtr>,
#[live]
selects_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust(0)]
page_no: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
step_page_offset: f64,
#[animator]
animator: Animator,
}
impl LiveHook for Steps {
fn after_new_from_doc(&mut self, cx: &mut Cx) {
// log!("Steps::after_new_from_doc");
self.page_no = 0; // Assign initial page
if self.page_no < self.page_titles.len() as u8 {
for (idx, title_text) in self.page_titles.iter().enumerate() {
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
if usize::from(self.page_no) == idx {
page.label(id!(step_title))
.set_text(cx, &format!("{}", &title_text.as_str()));
}
}
}
}
}
impl Widget for Steps {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, page) in self.pages.iter_mut() {
page.handle_event(cx, event, scope);
}
self.widget_match_event(cx, event, scope);
if self.animator_handle_event(cx, event).must_redraw() {
self.redraw(cx);
}
// Fire the "show" animation when the "restart" animation is done
if self
.animator
.animator_in_state(cx, id!(step_choice.restart))
{
self.animator_play(cx, id!(step_choice.show));
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl WidgetMatchEvent for Steps {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
for action in actions {
if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
let screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
log!("SCREEN_WIDTH ===> {}", screen_width);
}
if let NextBackAction::NextButton { clicked, page_no } =
action.as_widget_action().cast()
{
if clicked {
self.increment_page_no(cx); // Increment the page number
}
}
if let NextBackAction::BackButton { clicked, page_no } =
action.as_widget_action().cast()
{
if clicked {
self.decrement_page_no(cx); // Increment the page number
}
}
}
{
let mut arr: Vec<String> = self.page_titles.clone();
if (self.page_no as usize) < arr.len() {
// Iterate over the selected page's choices
for (inneridx, choices_arr) in arr.iter().enumerate() {
let offset: f64 = self.step_page_offset - SELECT_MAX_OFFSET;
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
if usize::from(self.page_no) == inneridx {
page.label(id!(step_title))
.set_text(cx, &format!("{}", &choices_arr.as_str()));
}
// page.label(id!(step_title))
// .set_text(cx, &format!("{}", &choices_arr.as_str()))
}
} else {
log!("Invalid page_no: {}", self.page_no);
}
}
}
}
impl Steps {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
cx.begin_turtle(walk, Layout::default());
// Steps
{
// Ensure page_no is within the bounds of select_choices_values
if (self.page_no as usize) < self.page_titles.len() {
let offset: f64 = self.step_page_offset - SELECT_MAX_OFFSET;
log!("Steps::draw_walkd-{:?}", self.page_no as usize);
// Iterate over the current page's choices
for (inneridx, choices_arr) in self.page_titles.iter().enumerate() {
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
if usize::from(self.page_no) == inneridx {
page.label(id!(step_title))
.set_text(cx, &format!("{}", &choices_arr.as_str()));
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
// Draw each widget with margin adjustment
}
} else {
log!("Invalid page_no: {}", self.page_no);
}
}
cx.end_turtle_with_area(&mut self.area);
// cx.end_turtle_with_area(&mut self.custom_button.area());
}
pub fn increment_page_no(&mut self, cx: &mut Cx) {
self.page_no = (self.page_no + 1).min(2); // Ensure page_no doesn't exceed max value
log!("SelectOption - Navigated to page: {}", self.page_no);
self.update_view(cx);
}
pub fn decrement_page_no(&mut self, cx: &mut Cx) {
if self.page_no > 0 {
self.page_no -= 1;
log!("SelectOption - Navigated to page: {}", self.page_no);
self.update_view(cx);
}
}
fn update_view(&mut self, cx: &mut Cx) {
match self.page_no {
0 => log!("SelectOption - Showing Step One"),
1 => log!("SelectOption - Showing Step Two"),
2 => log!("SelectOption - Showing Step Three"),
_ => log!("SelectOption - Unknown Page"),
}
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.page_titles.len() as u8;
log!("Steps::update_current_page: {:?}", &self.current_page);
// self.current_page = (self.current_page + 1) % self.choices_values.len() as u8;
}
}

31
src/shared/helpers.rs Normal file
View file

@ -0,0 +1,31 @@
use makepad_widgets::*;
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::styles::*;
pub Divider = <View> {
width: Fill, height: Fit
flow: Down
<RoundedView> {
width: Fill,
height: 1.,
draw_bg: {color: (#ddd)}
}
}
pub LineH = <RoundedView> {
width: Fill,
height: 2.0,
margin: 0.0,
padding: 0.0, spacing: 0.0
show_bg: true
draw_bg: {color: (COLOR_DIVIDER)}
}
pub FillerX = <View> { width: Fill, height: Fit }
pub FillerY = <View> { width: Fit, height: Fill }
}

View file

@ -0,0 +1,377 @@
//! A `HtmlOrPlaintext` view can display either plaintext or rich HTML content.
use makepad_widgets::{makepad_html::HtmlDoc, *};
/// The color of the text used to print the spoiler reason before the hidden text.
const COLOR_SPOILER_REASON: Vec4 = vec4(0.6, 0.6, 0.6, 1.0);
live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;
use crate::shared::styles::*;
// These match the `MESSAGE_*` styles defined in `styles.rs`.
// For some reason, they're not the same. That's TBD.
// HTML_LINE_SPACING = 6.0
// HTML_TEXT_HEIGHT_FACTOR = 1.1
// This is an HTML subwidget used to handle `<font>` and `<span>` tags,
// specifically: foreground text color, background color, and spoilers.
pub MatrixHtmlSpan = {{MatrixHtmlSpan}} {
width: Fit, height: Fit,
align: {x: 0., y: 0.}
}
// A centralized widget where we define styles and custom elements for HTML
// message content. This is a wrapper around Makepad's built-in `Html` widget.
pub MessageHtml = <Html> {
padding: 0.0,
width: Fill, height: Fit, // see comment in `HtmlOrPlaintext`
font_size: (MESSAGE_FONT_SIZE),
font_color: (MESSAGE_TEXT_COLOR),
draw_normal: { color: (MESSAGE_TEXT_COLOR), } // text_style: { height_factor: (HTML_TEXT_HEIGHT_FACTOR), line_spacing: (HTML_LINE_SPACING) } }
draw_italic: { color: (MESSAGE_TEXT_COLOR), } // text_style: { height_factor: (HTML_TEXT_HEIGHT_FACTOR), line_spacing: (HTML_LINE_SPACING) } }
draw_bold: { color: (MESSAGE_TEXT_COLOR), } // text_style: { height_factor: (HTML_TEXT_HEIGHT_FACTOR), line_spacing: (HTML_LINE_SPACING) } }
draw_bold_italic: { color: (MESSAGE_TEXT_COLOR), } // text_style: { height_factor: (HTML_TEXT_HEIGHT_FACTOR), line_spacing: (HTML_LINE_SPACING) } }
draw_fixed: { color: (MESSAGE_TEXT_COLOR), } // text_style: { height_factor: (HTML_TEXT_HEIGHT_FACTOR), line_spacing: (HTML_LINE_SPACING) } }
draw_block: {
line_color: (MESSAGE_TEXT_COLOR)
sep_color: (MESSAGE_TEXT_COLOR)
code_color: (#EDEDED)
quote_bg_color: (#EDEDED)
quote_fg_color: (MESSAGE_TEXT_COLOR)
}
// list_item_layout: { padding: {left: 5.0, top: 1.0, bottom: 1.0}, }
// code_layout: { padding: {left: 7.0, right: 7.0, top: 8.0, bottom: 0.0}, }
// quote_layout: { padding: {top: 0.0, bottom: 0.0}, }
// inline_code_padding: { left: 5.0, right: 5.0, top: 7.0, bottom: 0.0 }
font = <MatrixHtmlSpan> { }
span = <MatrixHtmlSpan> { }
a = {
hover_color: #21b070
grab_key_focus: false,
padding: {left: 1.0, right: 1.5},
}
body: "[<i> HTML message placeholder</i>]",
}
// A view container that displays either plaintext s(a simple `Label`)
// or rich HTML content (an instance of `MessageHtml`).
//
// Key Usage Notes:
// * Labels need their width to be Fill *and* all of their parent views
// also need to have their width set to Fill. Otherwise, the label
// won't wrap text properly.
// * They also need their height to be Fit along with all of their parent views,
// otherwise their total height will be zero (when a Fit is inside of a Fill),
// resulting in nothing being displayed.
pub HtmlOrPlaintext = {{HtmlOrPlaintext}} {
width: Fill, height: Fit, // see above comment
flow: Overlay
plaintext_view = <View> {
visible: true,
width: Fill, height: Fit, // see above comment
pt_label = <Label> {
width: Fill, height: Fit, // see above comment
draw_text: {
wrap: Word,
color: (MESSAGE_TEXT_COLOR),
text_style: <MESSAGE_TEXT_STYLE> { font_size: (MESSAGE_FONT_SIZE) },
}
text: "[plaintext message placeholder]",
}
}
html_view = <View> {
visible: false,
width: Fill, height: Fit, // see above comment
html = <MessageHtml> {}
}
}
}
/// A widget used to display a single HTML `<span>` tag or a `<font>` tag.
#[derive(Live, Widget)]
struct MatrixHtmlSpan {
// TODO: this is unused; just here to invalidly satisfy the area provider.
// I'm not sure how to implement `fn area()` given that it has multiple area rects.
#[redraw]
#[area]
area: Area,
// TODO: remove these if they're unneeded
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[rust]
drawn_areas: SmallVec<[Area; 2]>,
/// Whether to grab key focus when pressed.
#[live(true)]
grab_key_focus: bool,
/// The text content within the `<span>` tag.
#[live]
text: ArcStringMut,
/// The current display state of the spoiler.
#[rust]
spoiler: SpoilerDisplay,
/// Foreground (text) color: the `data-mx-color` or `color` attributes.
#[rust]
fg_color: Option<Vec4>,
/// Background color: the `data-mx-bg-color` attribute.
#[rust]
bg_color: Option<Vec4>,
}
/// The possible states that a spoiler can be in: hidden or revealed.
///
/// The enclosed `reason` string is an optional reason given for why
/// the text is hidden; if empty, then no reason was given.
#[derive(Default, Debug)]
enum SpoilerDisplay {
/// There is no spoiler at all.
#[default]
None,
/// The spoiler text is hidden, with an optional reason given.
Hidden { reason: String },
/// The spoiler text is revealed, with an optional reason given.
Revealed { reason: String },
}
impl SpoilerDisplay {
/// Toggles the spoiler's display state.
fn toggle(&mut self) {
match self {
SpoilerDisplay::Hidden { reason } => {
let s = std::mem::take(reason);
*self = SpoilerDisplay::Revealed { reason: s };
}
SpoilerDisplay::Revealed { reason } => {
let s = std::mem::take(reason);
*self = SpoilerDisplay::Hidden { reason: s };
}
SpoilerDisplay::None => {}
}
}
/// Returns `true` if this spoiler is not `None`, i.e., if it's `Hidden` or `Revealed`.
fn is_some(&self) -> bool {
!matches!(self, SpoilerDisplay::None)
}
}
impl LiveHook for MatrixHtmlSpan {
// After an MatrixHtmlSpan instance has been instantiated ("applied"),
// populate its struct fields from the `<span>` or `<font>` tag's attributes.
fn after_apply(&mut self, _cx: &mut Cx, apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// The attributes we care about (we allow all attributes in both tags):
// * in `<font>` tags: `color`
// * in `<span>` tags: `data-mx-color`, `data-mx-bg-color`, `data-mx-spoiler`
if let ApplyFrom::NewFromDoc { .. } = apply.from {
if let Some(scope) = apply.scope.as_ref() {
if let Some(doc) = scope.props.get::<HtmlDoc>() {
let mut walker = doc.new_walker_with_index(scope.index + 1);
while let Some((lc, attr)) = walker.while_attr_lc() {
let attr = attr.trim_matches(['"', '\'']);
match lc {
live_id!(color) | live_id!(data - mx - color) => {
self.fg_color = Vec4::from_hex_str(attr).ok()
}
live_id!(data - mx - bg - color) => {
self.bg_color = Vec4::from_hex_str(attr).ok()
}
live_id!(data - mx - spoiler) => {
self.spoiler = SpoilerDisplay::Hidden {
reason: attr.into(),
}
}
_ => (),
}
}
}
} else {
error!(
"BUG: MatrixHtmlSpan::after_apply(): scope not found, cannot set attributes."
);
}
}
}
}
impl Widget for MatrixHtmlSpan {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
let mut needs_redraw = false;
for area in self.drawn_areas.clone().into_iter() {
match event.hits(cx, area) {
Hit::FingerDown(_fe) if self.grab_key_focus => {
cx.set_key_focus(self.area());
}
Hit::FingerHoverIn(_) if self.spoiler.is_some() => {
cx.set_cursor(MouseCursor::Hand);
}
Hit::FingerUp(fe) if fe.is_over => {
self.spoiler.toggle();
needs_redraw = true;
}
_ => (),
}
}
if needs_redraw {
for area in &self.drawn_areas {
cx.redraw_area(*area);
}
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, _walk: Walk) -> DrawStep {
let Some(tf) = scope.data.get_mut::<TextFlow>() else {
return DrawStep::done();
};
// Here: the text flow has already began drawing,
// so we just need to tweak the formatting and draw the text.
tf.areas_tracker.push_tracker();
let mut pushed_color = false;
let mut pushed_inline_code = false;
let mut old_code_color = None;
if let Some(fg_color) = self.fg_color {
tf.font_colors.push(fg_color);
pushed_color = true;
}
if let Some(bg_color) = self.bg_color {
// Reuse the inline code drawblock to set the background color.
tf.inline_code.push();
pushed_inline_code = true;
old_code_color = Some(tf.draw_block.code_color);
tf.draw_block.code_color = bg_color;
}
match &self.spoiler {
SpoilerDisplay::Hidden { reason } | SpoilerDisplay::Revealed { reason } => {
// Draw the spoiler reason text in an italic gray font.
tf.font_colors.push(COLOR_SPOILER_REASON);
tf.italic.push();
// tf.push_size_rel_scale(0.8);
if reason.is_empty() {
tf.draw_text(cx, " [Spoiler] ");
} else {
tf.draw_text(cx, &format!(" [Spoiler: {}] ", reason));
}
// tf.font_sizes.pop();
tf.italic.pop();
tf.font_colors.pop();
// Now, draw the spoiler context text itself, either hidden or revealed.
if matches!(self.spoiler, SpoilerDisplay::Hidden { .. }) {
// Use a background color that is the same as the foreground color,
// which is a hacky way to make the spoiled text non-readable.
// In the future, we should use a proper blur effect.
let spoiler_bg_color = self
.fg_color
.or_else(|| tf.font_colors.last().copied())
.unwrap_or(tf.font_color);
tf.inline_code.push();
let old_bg_color = tf.draw_block.code_color;
tf.draw_block.code_color = spoiler_bg_color;
tf.draw_text(cx, self.text.as_ref());
tf.draw_block.code_color = old_bg_color;
tf.inline_code.pop();
} else {
tf.draw_text(cx, self.text.as_ref());
}
}
SpoilerDisplay::None => {
tf.draw_text(cx, self.text.as_ref());
}
}
if pushed_color {
tf.font_colors.pop();
}
if pushed_inline_code {
tf.inline_code.pop();
}
if let Some(old_code_color) = old_code_color {
tf.draw_block.code_color = old_code_color;
}
let (start, end) = tf.areas_tracker.pop_tracker();
self.drawn_areas = SmallVec::from(&tf.areas_tracker.areas[start..end]);
DrawStep::done()
}
fn text(&self) -> String {
self.text.as_ref().to_string()
}
fn set_text(&mut self, cx: &mut Cx, v: &str) {
self.text.as_mut_empty().push_str(v);
self.redraw(cx);
}
}
#[derive(LiveHook, Live, Widget)]
pub struct HtmlOrPlaintext {
#[deref]
view: View,
}
impl Widget for HtmlOrPlaintext {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope)
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
impl HtmlOrPlaintext {
/// Sets the plaintext content and makes it visible, hiding the rich HTML content.
pub fn show_plaintext<T: AsRef<str>>(&mut self, cx: &mut Cx, text: T) {
self.view(id!(html_view)).set_visible(cx, false);
self.view(id!(plaintext_view)).set_visible(cx, true);
self.label(id!(plaintext_view.pt_label))
.set_text(cx, text.as_ref());
}
/// Sets the HTML content, making the HTML visible and the plaintext invisible.
pub fn show_html<T: AsRef<str>>(&mut self, cx: &mut Cx, html_body: T) {
self.html(id!(html_view.html))
.set_text(cx, html_body.as_ref());
self.view(id!(html_view)).set_visible(cx, true);
self.view(id!(plaintext_view)).set_visible(cx, false);
}
}
impl HtmlOrPlaintextRef {
/// See [`HtmlOrPlaintext::show_plaintext()`].
pub fn show_plaintext<T: AsRef<str>>(&self, cx: &mut Cx, text: T) {
if let Some(mut inner) = self.borrow_mut() {
inner.show_plaintext(cx, text);
}
}
/// See [`HtmlOrPlaintext::show_html()`].
pub fn show_html<T: AsRef<str>>(&self, cx: &mut Cx, html_body: T) {
if let Some(mut inner) = self.borrow_mut() {
inner.show_html(cx, html_body);
}
}
}

View file

@ -1,9 +1,15 @@
use makepad_widgets::Cx;
pub mod choose;
pub mod helpers;
pub mod html_or_plaintext;
pub mod styles;
pub mod widgets;
pub fn live_design(cx: &mut Cx) {
styles::live_design(cx);
html_or_plaintext::live_design(cx);
helpers::live_design(cx);
choose::live_design(cx);
widgets::live_design(cx);
}

View file

@ -78,6 +78,7 @@ live_design! {
pub ROOM_NAME_TEXT_COLOR = #x0
pub COLOR_META = #xccc
pub TERTIARY_TEXT_COLOR = #x9ca3af
pub COLOR_PROFILE_CIRCLE = #xfff8ee
pub COLOR_DIVIDER = #x00000018
@ -88,7 +89,7 @@ live_design! {
pub COLOR_PRIMARY = #ffffff
pub COLOR_PRIMARY_DARKER = #fefefe
pub COLOR_SECONDARY = #eef2f4
pub COLOR_SECONDARY = #eef2ff
pub COLOR_SELECTED_PRIMARY = #0f88fe
pub COLOR_SELECTED_PRIMARY_DARKER = #106fcc

View file

@ -55,6 +55,62 @@ live_design! {
}
}
}
pub SmallCard = <RoundedView> {
width: Fit,
height: 200,
padding: {top: 30, bottom: 30, left: 20, right: 40}
// padding: 20
draw_bg: {
instance radius: 4.0,
color: #fff
}
content = <View>{
width: Fit,
height: Fill,
flow: Down
spacing: 20,
title = <Label> {
draw_text: {
wrap: Word
text_style: <BOLD_FONT>{font_size: 13},
color: #x0
}
}
description = <Label> {
width: 200
draw_text: {
wrap: Word
text_style: <REGULAR_FONT>{font_size: 13},
color: #x0
}
}
}
}
pub Avatar = <RoundedView> {
width: 24,
height: 24,
show_bg: true,
draw_bg: {
color: #444D9A,
radius: 6,
}
align: {x: 0.5, y: 0.5},
avatar_label = <Label> {
width: Fit,
height: Fit,
draw_text:{
text_style: <BOLD_FONT>{font_size: 10},
color: #fff,
}
text: "P"
}
}
pub SIDEBAR_FONT_COLOR = #344054
pub SIDEBAR_FONT_COLOR_HOVER = #344054