first commit
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/dist/
|
||||
/target/
|
||||
/Cargo.lock
|
26
Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
|||
[package]
|
||||
name = "simplestacknavigation"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[profile.small]
|
||||
inherits = "release"
|
||||
#debug = true
|
||||
opt-level = 'z' # Optimize for size
|
||||
lto = true # Enable link-time optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations
|
||||
panic = 'abort' # Abort on panic
|
||||
strip = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
makepad-widgets = { version = "0.6.0" }
|
||||
# makepad-widgets = { git = "https://github.com/makepad/makepad/", branch = "rik" }
|
34
README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Makepad UI
|
||||
|
||||
This template should help get you started developing with Makepad Rust UI.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
|
||||
|
||||
|
||||
# Desktop in Debug Mode
|
||||
cargo run
|
||||
|
||||
# Desktop in Release Mode
|
||||
cargo run --release
|
||||
|
||||
# Desktop in small size
|
||||
cargo run --profile=small
|
||||
|
||||
# Android
|
||||
cargo makepad android run --release
|
||||
|
||||
# IOS Simulator
|
||||
cargo makepad apple ios --org=my.test --app=makepad-template run-sim --release
|
||||
|
||||
# IOS Device
|
||||
cargo makepad apple ios --org-id=123456 --org=my.test --app=makepad-template run-device makepad-template --release
|
||||
|
||||
# Cargo Check Builds
|
||||
cargo makepad check install-toolchain
|
||||
cargo makepad check all
|
||||
|
||||
cargo makepad wasm install-toolchain
|
||||
cargo makepad apple ios install-toolchain
|
||||
cargo makepad android --abi=all install-toolchain
|
34
index.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>Makepad Template</title>
|
||||
<script type='module'>
|
||||
import {WasmWebGL} from "/makepad-template/platform/src/os/web/web_gl.js"
|
||||
|
||||
const wasm = await WasmWebGL.fetch_and_instantiate_wasm(
|
||||
"makepad-template/target/wasm32-unknown-unknown/release/login.wasm"
|
||||
);
|
||||
|
||||
class MyWasmApp {
|
||||
constructor(wasm) {
|
||||
let canvas = document.getElementsByClassName('full_canvas')[0];
|
||||
this.webgl = new WasmWebGL (wasm, this, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
let app = new MyWasmApp(wasm);
|
||||
</script>
|
||||
<script type='module' src='/makepad-template/platform/src/os/web/auto_reload.js'></script>
|
||||
<link rel='stylesheet' type='text/css' href='/makepad-template/platform/src/os/web/full_canvas.css'>
|
||||
</head>
|
||||
<body>
|
||||
<canvas class='full_canvas'></canvas>
|
||||
<div class='canvas_loader' >
|
||||
<div style=''>
|
||||
Loading..
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
4
resources/icons/add_contact.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 26.12 478.318 C 27.455 371.36 111.726 246.106 262.142 245.254 C 405.325 244.252 468.365 358.156 472.358 476.201 Z M 361.132 148.48 C 361.672 200.615 318.234 237.596 265.693 237.952 C 205.763 238.358 141.023 201.499 141.023 141.567 C 141.023 81.635 203.446 37.329 256.008 37.901 C 315.875 38.552 361.22 99.215 361.132 148.48 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 477 B |
4
resources/icons/back.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 353.56 52.54 L 112.646 233.14 L 82.765 255.541 L 112.842 277.727 L 355.107 456.442 L 395.549 411.711 L 183.36 255.183 L 394.393 96.983 L 353.56 52.54 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 303 B |
4
resources/icons/chat.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 485.952 219.206 C 485.952 309.503 387.472 382.704 265.99 382.704 C 231.496 382.704 198.857 376.802 169.799 366.283 C 129.409 399.528 43.827 448.824 28.821 443.232 C 36.136 445.451 92.393 358.523 100.398 326.828 C 66.544 298.072 46.028 260.42 46.028 219.206 C 46.028 128.909 144.508 55.708 265.99 55.708 C 387.472 55.708 485.952 128.909 485.952 219.206 Z M 28.701 443.186 C 28.74 443.202 28.78 443.217 28.821 443.232 C 28.779 443.22 28.739 443.204 28.701 443.186 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 616 B |
4
resources/icons/contacts.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 8.519 478.559 C 9.706 374.681 84.66 253.033 218.445 252.205 C 345.797 251.232 401.867 361.857 405.418 476.503 L 8.519 478.559 Z M 306.49 158.218 C 306.971 208.852 268.335 244.768 221.603 245.114 C 168.3 245.508 110.718 209.71 110.718 151.504 C 110.718 93.297 166.239 50.267 212.989 50.822 C 266.237 51.455 306.568 110.371 306.49 158.218 Z M 364.323 153.275 L 479.38 153.214 L 479.386 164.214 L 364.329 164.275 L 364.323 153.275 Z M 481.582 229.879 L 367.499 229.543 L 367.531 218.543 L 481.614 218.879 L 481.582 229.879 Z M 366.138 296.715 L 482.243 296.612 L 482.253 307.612 L 366.148 307.715 L 366.138 296.715 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 763 B |
4
resources/icons/discover.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 462.932 247.921 C 462.28 317.262 383.634 461.241 260.208 451.248 C 118.81 439.799 71.095 346.598 72.526 241.826 C 74.423 102.936 192.292 42.705 260.208 44.594 C 411.229 48.794 463.584 178.58 462.932 247.921 Z M 336.948 226.044 L 382.363 110.526 L 274.785 176.882 Z M 269.576 196.661 C 245.557 196.661 226.085 217.9 226.085 244.099 C 226.085 270.298 245.557 291.537 269.576 291.537 C 293.595 291.537 313.067 270.298 313.067 244.099 C 313.067 217.9 293.595 196.661 269.576 196.661 Z M 203.842 263.902 L 161.467 390.659 L 271.348 306.072 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 689 B |
5
resources/icons/icon_home.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 23 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="M 26.12 478.318 C 27.455 371.36 111.726 246.106 262.142 245.254 C 405.325 244.252 468.365 358.156 472.358 476.201 L 26.12 478.318 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 598 B |
4
resources/icons/me.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 26.12 478.318 C 27.455 371.36 111.726 246.106 262.142 245.254 C 405.325 244.252 468.365 358.156 472.358 476.201 Z M 361.132 148.48 C 361.672 200.615 318.234 237.596 265.693 237.952 C 205.763 238.358 141.023 201.499 141.023 141.567 C 141.023 81.635 203.446 37.329 256.008 37.901 C 315.875 38.552 361.22 99.215 361.132 148.48 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 477 B |
1
resources/icons/menu.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> <title>ic_fluent_add_circle_28_regular</title> <desc>Created with Sketch.</desc> <g id="🔍-System-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="ic_fluent_add_circle_28_regular" fill="#212121" fill-rule="nonzero"> <path d="M14,2 C20.627417,2 26,7.372583 26,14 C26,20.627417 20.627417,26 14,26 C7.372583,26 2,20.627417 2,14 C2,7.372583 7.372583,2 14,2 Z M14,3.5 C8.20101013,3.5 3.5,8.20101013 3.5,14 C3.5,19.7989899 8.20101013,24.5 14,24.5 C19.7989899,24.5 24.5,19.7989899 24.5,14 C24.5,8.20101013 19.7989899,3.5 14,3.5 Z M14,8 C14.4142136,8 14.75,8.33578644 14.75,8.75 L14.75,13.25 L19.25,13.25 C19.6642136,13.25 20,13.5857864 20,14 C20,14.4142136 19.6642136,14.75 19.25,14.75 L14.75,14.75 L14.75,19.25 C14.75,19.6642136 14.4142136,20 14,20 C13.5857864,20 13.25,19.6642136 13.25,19.25 L13.25,14.75 L8.75,14.75 C8.33578644,14.75 8,14.4142136 8,14 C8,13.5857864 8.33578644,13.25 8.75,13.25 L13.25,13.25 L13.25,8.75 C13.25,8.33578644 13.5857864,8 14,8 Z" id="🎨-Color"> </path> </g> </g> </g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
5
resources/icons/money.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 23 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="M11.391,4.904C11.465,4.83 11.576,4.793 11.688,4.793C11.762,4.793 11.873,4.83 11.947,4.904L18.813,10.508L18.813,16.594C18.813,16.928 18.516,17.188 18.219,17.188L14.025,17.188C13.729,17.188 13.432,16.928 13.432,16.594L13.432,13.031C13.432,12.734 13.172,12.438 12.838,12.438L10.463,12.438C10.166,12.438 9.869,12.734 9.869,13.031L9.869,16.594C9.869,16.928 9.609,17.188 9.313,17.188L5.156,17.188C4.822,17.188 4.563,16.928 4.563,16.594L4.563,10.545L11.391,4.904ZM22.19,8.727C22.301,8.801 22.375,8.949 22.375,9.061C22.375,9.172 22.338,9.283 22.264,9.357L21.299,10.508C21.225,10.619 21.113,10.656 20.965,10.656C20.854,10.656 20.742,10.619 20.668,10.545L11.947,3.383C11.873,3.309 11.762,3.271 11.688,3.271C11.576,3.271 11.465,3.309 11.391,3.383L2.67,10.545C2.596,10.619 2.484,10.656 2.373,10.656C2.225,10.656 2.113,10.619 2.039,10.508L1.074,9.357C1.037,9.283 0.963,9.172 0.963,9.061C0.963,8.949 1.037,8.801 1.148,8.727L10.537,0.971C10.834,0.748 11.242,0.6 11.688,0.6C12.096,0.6 12.504,0.748 12.801,0.971L16.141,3.717L16.141,1.045C16.141,0.785 16.326,0.6 16.586,0.6L18.664,0.6C18.887,0.6 19.109,0.785 19.109,1.045L19.109,6.166L22.19,8.727Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
7
resources/icons/scan.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 19 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(-1,0,0,1,18.9721,0)">
|
||||
<path d="M18.666,6.426C19.074,6.797 19.074,7.428 18.666,7.799L12.135,13.44C11.578,13.922 10.688,13.514 10.688,12.734L10.688,9.506C4.898,9.58 2.449,10.99 4.119,16.371C4.305,16.965 3.563,17.447 3.08,17.076C1.447,15.889 0,13.625 0,11.361C0,5.721 4.713,4.496 10.688,4.459L10.688,1.49C10.688,0.711 11.578,0.303 12.135,0.785L18.666,6.426Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 877 B |
5
resources/icons/search.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 19 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1;">
|
||||
<path d="M18.74,16.815C19.074,17.186 19.074,17.742 18.703,18.076L17.664,19.115C17.33,19.486 16.773,19.486 16.402,19.115L12.729,15.441C12.543,15.256 12.469,15.033 12.469,14.81L12.469,14.18C11.133,15.219 9.5,15.813 7.719,15.813C3.451,15.813 0,12.361 0,8.094C0,3.863 3.451,0.375 7.719,0.375C11.949,0.375 15.438,3.863 15.438,8.094C15.438,9.912 14.807,11.545 13.805,12.844L14.398,12.844C14.621,12.844 14.844,12.955 15.029,13.104L18.74,16.815ZM7.719,12.844C10.316,12.844 12.469,10.729 12.469,8.094C12.469,5.496 10.316,3.344 7.719,3.344C5.084,3.344 2.969,5.496 2.969,8.094C2.969,10.729 5.084,12.844 7.719,12.844Z" style="fill-rule:nonzero; stroke-width: 0.5;"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/makepad.png
Normal file
After Width: | Height: | Size: 22 KiB |
42
resources/makepad.svg
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 7067 1035" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-54.1122,-304.466)">
|
||||
<!-- <rect id="ArtBoard5" x="54.112" y="304.466" width="7066.93" height="1034.07" style="fill:none;"/> -->
|
||||
<g>
|
||||
<clipPath id="_clip1">
|
||||
<rect x="54.112" y="304.466" width="7066.93" height="1034.07"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#_clip1)">
|
||||
<g transform="matrix(2.46433,0,0,0.355348,0.112216,246.617)">
|
||||
<g transform="matrix(0.412987,0,0,2.51071,-14964.8,-23383.5)">
|
||||
<path d="M37739.3,9977.06L38019.7,10537.9L37458.9,10537.9L37739.3,9977.06Z" style="fill:rgb(255,92,57);"/>
|
||||
</g>
|
||||
<g transform="matrix(-0.412987,0,0,2.51071,15723.6,-23383.5)">
|
||||
<path d="M37739.3,9977.06L38019.7,10537.9L37458.9,10537.9L37739.3,9977.06Z" style="fill:rgb(255,92,57);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.412987,0,0,2.51071,-15087.3,-24885.4)">
|
||||
<path d="M37739.3,9977.06L38019.7,10537.9L37458.9,10537.9L37739.3,9977.06Z" style="fill:rgb(255,92,57);"/>
|
||||
</g>
|
||||
<g transform="matrix(-0.412987,0,0,2.51071,15846,-24885.4)">
|
||||
<path d="M37739.3,9977.06L38019.7,10537.9L37458.9,10537.9L37739.3,9977.06Z" style="fill:rgb(255,92,57);"/>
|
||||
</g>
|
||||
<g transform="matrix(-0.412987,-3.50746e-16,4.43366e-17,-2.51071,16084.4,28088.3)">
|
||||
<path d="M37739.3,9977.06L38019.7,10537.9L37458.9,10537.9L37739.3,9977.06Z" style="fill:rgb(255,92,57);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.412987,-3.50746e-16,-4.43366e-17,-2.51071,-15325.6,28088.3)">
|
||||
<path d="M37739.3,9977.06L38019.7,10537.9L37458.9,10537.9L37739.3,9977.06Z" style="fill:rgb(255,92,57);"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(83.3091,0,0,83.3091,-43233.8,-47386.6)">
|
||||
<path d="M544.422,582.292L546.018,582.292L546.63,579.016C546.75,578.38 546.87,577.936 547.026,577.672C547.302,577.204 547.746,576.952 548.31,576.952C548.718,576.952 549.054,577.096 549.222,577.348C549.318,577.492 549.366,577.684 549.366,577.948C549.366,578.14 549.33,578.368 549.282,578.644L548.61,582.292L550.206,582.292L550.818,579.016C550.95,578.296 551.07,577.924 551.274,577.624C551.574,577.192 552.018,576.952 552.534,576.952C552.918,576.952 553.23,577.084 553.398,577.336C553.506,577.48 553.554,577.672 553.554,577.948C553.554,578.14 553.518,578.368 553.47,578.644L552.798,582.292L554.394,582.292L555.102,578.452C555.162,578.116 555.198,577.804 555.198,577.528C555.198,577.168 555.15,576.856 555.054,576.604C554.766,575.896 553.998,575.488 552.954,575.488C552.018,575.488 551.31,575.8 550.638,576.52C550.314,575.788 549.798,575.488 548.874,575.488C548.094,575.488 547.578,575.704 547.002,576.268L547.122,575.644L545.658,575.644L544.422,582.292Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
<path d="M563.61,575.644L562.146,575.644L561.978,576.532C561.558,575.8 560.958,575.488 559.998,575.488C558.03,575.488 556.338,576.964 555.966,578.992C555.918,579.244 555.894,579.484 555.894,579.724C555.894,581.344 556.986,582.448 558.678,582.448C559.614,582.448 560.298,582.16 561.066,581.428L560.91,582.292L562.374,582.292L563.61,575.644ZM559.866,576.952C560.874,576.952 561.522,577.588 561.522,578.548C561.522,578.692 561.498,578.836 561.474,578.992C561.258,580.18 560.322,580.984 559.17,580.984C558.15,580.984 557.526,580.372 557.526,579.448C557.526,579.316 557.538,579.172 557.562,579.028C557.79,577.828 558.75,576.952 559.866,576.952Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
<path d="M563.67,582.292L565.266,582.292L565.842,579.184L567.534,582.292L569.682,582.292L567.546,578.896L570.534,575.644L568.626,575.644L565.914,578.776L566.91,573.424L565.314,573.424L563.67,582.292Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
<path d="M576.918,579.676C577.014,579.448 577.05,579.304 577.098,579.064C577.146,578.8 577.17,578.548 577.17,578.308C577.17,576.64 576.054,575.488 574.326,575.488C572.37,575.488 570.534,577.036 570.174,578.992C570.138,579.208 570.114,579.424 570.114,579.628C570.114,581.26 571.338,582.448 573.138,582.448C574.23,582.448 575.142,582.064 575.994,581.248C576.306,580.936 576.522,580.66 576.69,580.324L574.95,580.324C574.458,580.804 574.026,580.984 573.366,580.984C572.418,580.984 571.818,580.48 571.77,579.676L576.918,579.676ZM571.986,578.272C572.394,577.408 573.126,576.952 574.074,576.952C575.058,576.952 575.622,577.42 575.67,578.272L571.986,578.272Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
<path d="M577.074,584.512L578.67,584.512L579.198,581.668C579.714,582.208 580.278,582.448 581.094,582.448C583.014,582.448 584.694,580.96 585.066,578.944C585.114,578.692 585.138,578.44 585.138,578.2C585.138,576.592 584.07,575.488 582.39,575.488C581.49,575.488 580.662,575.812 580.038,576.424L580.182,575.644L578.718,575.644L577.074,584.512ZM581.874,576.952C582.858,576.952 583.494,577.612 583.494,578.548C583.494,578.68 583.47,578.836 583.446,578.98C583.242,580.108 582.246,580.984 581.178,580.984C580.206,580.984 579.558,580.312 579.558,579.388C579.558,579.256 579.57,579.124 579.594,578.992C579.81,577.828 580.794,576.952 581.874,576.952Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
<path d="M593.37,575.644L591.906,575.644L591.738,576.532C591.318,575.8 590.718,575.488 589.758,575.488C587.79,575.488 586.098,576.964 585.726,578.992C585.678,579.244 585.654,579.484 585.654,579.724C585.654,581.344 586.746,582.448 588.438,582.448C589.374,582.448 590.058,582.16 590.826,581.428L590.67,582.292L592.134,582.292L593.37,575.644ZM589.626,576.952C590.634,576.952 591.282,577.588 591.282,578.548C591.282,578.692 591.258,578.836 591.234,578.992C591.018,580.18 590.082,580.984 588.93,580.984C587.91,580.984 587.286,580.372 587.286,579.448C587.286,579.316 587.298,579.172 587.322,579.028C587.55,577.828 588.51,576.952 589.626,576.952Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
<path d="M601.758,573.424L600.162,573.424L599.646,576.232C599.274,575.716 598.554,575.404 597.714,575.404C595.854,575.404 594.114,576.964 593.742,578.956C593.694,579.196 593.67,579.436 593.67,579.664C593.67,581.284 594.774,582.448 596.43,582.448C597.33,582.448 598.026,582.148 598.806,581.428L598.65,582.292L600.114,582.292L601.758,573.424ZM597.654,576.868C598.662,576.868 599.298,577.528 599.298,578.5C599.298,578.644 599.286,578.788 599.262,578.944C599.046,580.084 598.026,580.984 596.946,580.984C595.974,580.984 595.314,580.276 595.314,579.316C595.314,579.184 595.326,579.052 595.35,578.908C595.566,577.756 596.574,576.868 597.654,576.868Z" style="fill:rgb(250,225,188);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.1 KiB |
4
resources/me.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 26.12 478.318 C 27.455 371.36 111.726 246.106 262.142 245.254 C 405.325 244.252 468.365 358.156 472.358 476.201 Z M 361.132 148.48 C 361.672 200.615 318.234 237.596 265.693 237.952 C 205.763 238.358 141.023 201.499 141.023 141.567 C 141.023 81.635 203.446 37.329 256.008 37.901 C 315.875 38.552 361.22 99.215 361.132 148.48 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 477 B |
244
src/api.rs
Normal file
|
@ -0,0 +1,244 @@
|
|||
use makepad_widgets::makepad_platform::{LiveId, live_id};
|
||||
use std::iter;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ChatEntry {
|
||||
pub id: u64,
|
||||
pub username: String,
|
||||
pub avatar: LiveId,
|
||||
pub latest_message: MessagePreview,
|
||||
pub timestamp: String,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Counter {
|
||||
pub id: u64,
|
||||
pub username: String,
|
||||
pub counter: usize,
|
||||
pub avatar: LiveId,
|
||||
pub latest_message: MessagePreview,
|
||||
pub timestamp: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MessagePreview {
|
||||
Audio,
|
||||
Image,
|
||||
Video,
|
||||
Text(String),
|
||||
}
|
||||
|
||||
impl MessagePreview {
|
||||
pub fn text(&self) -> &str {
|
||||
match self {
|
||||
MessagePreview::Audio => "[Audio]",
|
||||
MessagePreview::Image => "[Image]",
|
||||
MessagePreview::Video => "[Video]",
|
||||
MessagePreview::Text(text) => text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MessageDirection {
|
||||
Outgoing,
|
||||
Incoming,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageEntry {
|
||||
pub direction: MessageDirection,
|
||||
pub chat_id: u64,
|
||||
pub avatar: LiveId,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub struct Db {
|
||||
messages: Vec<MessageEntry>,
|
||||
chats: Vec<ChatEntry>,
|
||||
counts: Vec<Counter>,
|
||||
}
|
||||
|
||||
impl Default for Db {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Db {
|
||||
pub fn new() -> Self {
|
||||
let messages: Vec<MessageEntry> = (0..200)
|
||||
.flat_map(|i| {
|
||||
vec![
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Incoming,
|
||||
avatar: live_id!(jorgebejar),
|
||||
chat_id: (i * 2) % 50 + 1,
|
||||
text: "体議速人幅触無持編聞組込".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Outgoing,
|
||||
avatar: live_id!(rikarends),
|
||||
chat_id: (i * 2) % 50 + 1,
|
||||
text: "減活乗治外進".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Incoming,
|
||||
avatar: live_id!(jorgebejar),
|
||||
chat_id: (i * 2) % 50 + 1,
|
||||
text: "福読併棋一御質慰".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Outgoing,
|
||||
avatar: live_id!(rikarends),
|
||||
chat_id: (i * 2) % 50 + 1,
|
||||
text: "嶋可済政実玉全強無示餌".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Outgoing,
|
||||
avatar: live_id!(johndoe),
|
||||
chat_id: (i * 2) % 50 + 2,
|
||||
text: "福読併棋一御質慰".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Incoming,
|
||||
avatar: live_id!(julianmontesdeoca),
|
||||
chat_id: (i * 2) % 50 + 2,
|
||||
text: "消再野誰強心無嶋可済実玉全示餌".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Outgoing,
|
||||
avatar: live_id!(johndoe),
|
||||
chat_id: (i * 2) % 50 + 2,
|
||||
text: "体議速人幅触無持編聞組込".to_string(),
|
||||
},
|
||||
MessageEntry {
|
||||
direction: MessageDirection::Incoming,
|
||||
avatar: live_id!(julianmontesdeoca),
|
||||
chat_id: (i * 2) % 50 + 2,
|
||||
text: "減活乗治外進".to_string(),
|
||||
},
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
Db {
|
||||
messages,
|
||||
chats: vec![
|
||||
ChatEntry {
|
||||
id: 1,
|
||||
username: "Olive Yew".to_string(),
|
||||
avatar: live_id!(rikarends),
|
||||
latest_message: MessagePreview::Text("Hi!".to_string()),
|
||||
timestamp: "14:09".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 2,
|
||||
username: "John Doe".to_string(),
|
||||
avatar: live_id!(johndoe),
|
||||
latest_message: MessagePreview::Image,
|
||||
timestamp: "11:20".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 3,
|
||||
username: "Peg Legge".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Audio,
|
||||
timestamp: "friday".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 4,
|
||||
username: "Barb Akew".to_string(),
|
||||
avatar: live_id!(julianmontesdeoca),
|
||||
latest_message: MessagePreview::Video,
|
||||
timestamp: "friday".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 5,
|
||||
username: "Chris P. Bacon".to_string(),
|
||||
avatar: live_id!(edwardtan),
|
||||
latest_message: MessagePreview::Text("thanks ed, see you there.".to_string()),
|
||||
timestamp: "thursday".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 6,
|
||||
username: "WeChat Team".to_string(),
|
||||
avatar: live_id!(wechatteam),
|
||||
latest_message: MessagePreview::Text("Welcome to WeChat!".to_string()),
|
||||
timestamp: "18/07".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 7,
|
||||
username: "Andrew Lin".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Text(
|
||||
"Awesome, I'll make sure they know about it".to_string(),
|
||||
),
|
||||
timestamp: "18/07".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 8,
|
||||
username: "Christian Huxley".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Image,
|
||||
timestamp: "15/07".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 9,
|
||||
username: "Ana Leddie".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Image,
|
||||
timestamp: "14/07".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 10,
|
||||
username: "Adam Adler".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Video,
|
||||
timestamp: "10/07".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 11,
|
||||
username: "Gabriel Hayes".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Text("wow I haven't seen that".to_string()),
|
||||
timestamp: "10/07".to_string(),
|
||||
},
|
||||
ChatEntry {
|
||||
id: 12,
|
||||
username: "Eric Ford".to_string(),
|
||||
avatar: live_id!(jorgebejar),
|
||||
latest_message: MessagePreview::Text("Nice to see you here!".to_string()),
|
||||
timestamp: "10/07".to_string(),
|
||||
},
|
||||
],
|
||||
counts: vec![
|
||||
Counter {
|
||||
id: 0,
|
||||
username: "".to_string(),
|
||||
count: 0,
|
||||
avatar: live_id!(rikarends),
|
||||
latest_message: MessagePreview::Text("Hi!".to_string()),
|
||||
timestamp: "00:00".to_string(),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_chats(&self) -> Vec<ChatEntry> {
|
||||
iter::repeat(self.chats.clone()).take(50).flatten().collect()
|
||||
}
|
||||
|
||||
pub fn get_chat(&self, chat_id: u64) -> Option<&ChatEntry> {
|
||||
self.chats.iter().find(|m| m.id == chat_id)
|
||||
}
|
||||
pub fn get_count(&self, chat_id: u64) -> Option<&ChatEntry> {
|
||||
self.chats.iter().find(|m| m.id == chat_id)
|
||||
}
|
||||
|
||||
pub fn get_messages_by_chat_id(&self, chat_id: u64) -> Vec<MessageEntry> {
|
||||
self.messages
|
||||
.iter()
|
||||
.filter(|m| m.chat_id == chat_id)
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
160
src/app.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use crate::shared::stack_navigation::*;
|
||||
use crate::shared::stack_view_action::StackViewAction;
|
||||
use makepad_widgets::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
live_design!{
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::welcome::welcome_screen::WelcomeScreen
|
||||
import crate::counter::counter_screen::CounterScreen
|
||||
|
||||
import crate::shared::clickable_view::ClickableView
|
||||
import crate::shared::stack_navigation::*;
|
||||
|
||||
import crate::shared::styles::*;
|
||||
|
||||
ICON_CHAT = dep("crate://self/resources/icons/chat.svg")
|
||||
|
||||
|
||||
AppTab = <RadioButton> {
|
||||
width: Fit,
|
||||
height: Fill,
|
||||
align: {x: 0.0, y: 0.0}
|
||||
draw_radio: {
|
||||
radio_type: Tab,
|
||||
color_active: #fff,
|
||||
color_inactive: #fff,
|
||||
}
|
||||
draw_text: {
|
||||
color_selected: #0b0,
|
||||
color_unselected: #000,
|
||||
color_unselected_hover: #111,
|
||||
text_style: <H3_TEXT_REGULAR> {}
|
||||
}
|
||||
}
|
||||
|
||||
App = {{App}} {
|
||||
|
||||
ui: <Window>{
|
||||
show_bg: true
|
||||
width: Fill,
|
||||
height: Fill
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
return mix(#7, #3, self.pos.y);
|
||||
}
|
||||
}
|
||||
body = {
|
||||
navigation = <StackNavigation> {
|
||||
root_view = {
|
||||
width: Fill,
|
||||
height: Fill,
|
||||
padding: 0, align: {x: 0.0, y: 0.0}, spacing: 0., flow: Down
|
||||
|
||||
application_pages = <View> {
|
||||
margin: 0.0,
|
||||
padding: 0.0
|
||||
|
||||
welcome_frame = <WelcomeScreen> { visible: true }
|
||||
}
|
||||
}
|
||||
my_counter_stack_view = <StackNavigationView> {
|
||||
header = {
|
||||
content = {
|
||||
title_container = {
|
||||
title = {
|
||||
text: "Counter"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<CounterScreen> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app_main!(App);
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct App {
|
||||
#[live] ui: WidgetRef,
|
||||
#[rust] counter: usize,
|
||||
#[rust] navigation_destinations: HashMap<StackViewAction, LiveId>,
|
||||
}
|
||||
|
||||
impl LiveHook for App {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
crate::makepad_widgets::live_design(cx);
|
||||
|
||||
// welcome
|
||||
crate::welcome::welcome_screen::live_design(cx);
|
||||
// counter
|
||||
crate::counter::counter_screen::live_design(cx);
|
||||
|
||||
// shared
|
||||
crate::shared::styles::live_design(cx);
|
||||
crate::shared::helpers::live_design(cx);
|
||||
crate::shared::header::live_design(cx);
|
||||
crate::shared::search_bar::live_design(cx);
|
||||
crate::shared::custom_button::live_design(cx);
|
||||
crate::shared::popup_menu::live_design(cx);
|
||||
crate::shared::dropdown_menu::live_design(cx);
|
||||
crate::shared::stack_navigation::live_design(cx);
|
||||
crate::shared::clickable_view::live_design(cx);
|
||||
}
|
||||
fn after_new_from_doc(&mut self, _cx: &mut Cx) {
|
||||
self.init_navigation_destinations();
|
||||
}
|
||||
}
|
||||
|
||||
impl App{
|
||||
fn init_navigation_destinations(&mut self) {
|
||||
self.navigation_destinations = HashMap::new();
|
||||
self.navigation_destinations.insert(StackViewAction::ShowCounterScreen, live_id!(my_counter_stack_view));
|
||||
}
|
||||
|
||||
async fn _do_network_request(_cx:CxRef, _ui:WidgetRef, _url:&str)->String{
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl AppMain for App{
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
|
||||
if let Event::Draw(event) = event {
|
||||
return self.ui.draw_widget_all(&mut Cx2d::new(cx, event));
|
||||
}
|
||||
let actions = self.ui.handle_widget_event(cx, event);
|
||||
|
||||
self.ui.radio_button_set(ids!(
|
||||
mobile_modes.tab1,
|
||||
))
|
||||
.selected_to_visible(
|
||||
cx,
|
||||
&self.ui,
|
||||
&actions,
|
||||
ids!(
|
||||
application_pages.welcome_frame,
|
||||
),
|
||||
);
|
||||
let mut navigation = self.ui.stack_navigation(id!(navigation));
|
||||
navigation.handle_stack_view_actions(
|
||||
cx,
|
||||
&actions,
|
||||
&self.navigation_destinations
|
||||
);
|
||||
|
||||
|
||||
if self.ui.button(id!(button1)).clicked(&actions) {
|
||||
log!("BUTTON CLICKED {}", self.counter);
|
||||
self.counter += 1;
|
||||
let label = self.ui.label(id!(label1));
|
||||
label.set_text_and_redraw(cx,&format!("Counter: {}", self.counter));
|
||||
}
|
||||
}
|
||||
}
|
136
src/counter/counter_screen.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
use crate::shared::clickable_view::*;
|
||||
use crate::shared::stack_view_action::StackViewAction;
|
||||
use makepad_widgets::widget::WidgetCache;
|
||||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::shared::helpers::FillerX;
|
||||
import crate::shared::helpers::Divider;
|
||||
import crate::shared::styles::*;
|
||||
import crate::shared::clickable_view::ClickableView
|
||||
// import crate::shared::header::HeaderDropDownMenu;
|
||||
|
||||
|
||||
|
||||
Counter = {{Counter}} {
|
||||
view: {
|
||||
width: Fill, height: Fit
|
||||
flow: Down, spacing: 10.
|
||||
|
||||
show_bg: true,
|
||||
draw_bg: {
|
||||
color: #ddd
|
||||
}
|
||||
// <CounterHeader> {}
|
||||
|
||||
body = <View>{
|
||||
|
||||
flow: Down,
|
||||
spacing: 20,
|
||||
align: {
|
||||
x: 0.5,
|
||||
y: 0.5
|
||||
},
|
||||
|
||||
avatar = <Image> {
|
||||
source: (LOGO_MAKEPAD),
|
||||
width: 300., height: 80.
|
||||
}
|
||||
button1 = <Button> {
|
||||
text: "Hello world"
|
||||
}
|
||||
input1 = <TextInput> {
|
||||
width: 100, height: 30
|
||||
text: "Click to count"
|
||||
}
|
||||
|
||||
label1 = <Label> {
|
||||
draw_text: {
|
||||
color: #f
|
||||
},
|
||||
text: "Counter: 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CounterScreen = <View> {
|
||||
width: Fill, height: Fill
|
||||
|
||||
<Counter> {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct Counter {
|
||||
#[live]
|
||||
view:View,
|
||||
// #[live] ui: WidgetRef,
|
||||
#[rust] counter: usize,
|
||||
|
||||
}
|
||||
|
||||
impl LiveHook for Counter {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, Counter);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Counter {
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
|
||||
|
||||
let uid = self.widget_uid();
|
||||
self.handle_event_with(cx, event, &mut |cx, action: StackViewAction| {
|
||||
dispatch_action(cx, WidgetActionItem::new(action.into(), uid));
|
||||
});
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.view.redraw(cx);
|
||||
}
|
||||
|
||||
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
|
||||
self.view.find_widgets(path, cached, results);
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.view.draw_walk_widget(cx, walk)
|
||||
}
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, StackViewAction),
|
||||
) {
|
||||
|
||||
let actions = self.view.handle_widget_event(cx, event);
|
||||
|
||||
if self
|
||||
.view
|
||||
.clickable_view(id!(my_profile_frame))
|
||||
.clicked(&actions)
|
||||
{
|
||||
dispatch_action(cx, StackViewAction::ShowMyProfile);
|
||||
}
|
||||
if self.view.button(id!(button1)).clicked(&actions) {
|
||||
log!("BUTTON CLICKED {}", self.counter);
|
||||
self.counter += 1;
|
||||
let label = self.view.label(id!(label1));
|
||||
label.set_text_and_redraw(cx,&format!("Counter: {}", self.counter));
|
||||
}
|
||||
}
|
||||
}
|
1
src/counter/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod counter_screen;
|
5
src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub use makepad_widgets;
|
||||
pub mod app;
|
||||
pub mod welcome;
|
||||
pub mod counter;
|
||||
pub mod shared;
|
6
src/main.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
// this stub is necessary because some platforms require building
|
||||
// as dll (mobile / wasm) and some require to be built as executable
|
||||
// unfortunately cargo doesn't facilitate this without a main.rs stub
|
||||
fn main(){
|
||||
simplestacknavigation::app::app_main()
|
||||
}
|
96
src/shared/clickable_view.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
use makepad_widgets::widget::WidgetCache;
|
||||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
ClickableView = {{ClickableView}} {
|
||||
width: Fit, height: Fit
|
||||
show_bg: true
|
||||
draw_bg: {
|
||||
color: #fff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct ClickableView {
|
||||
#[deref]
|
||||
view:View,
|
||||
}
|
||||
|
||||
impl LiveHook for ClickableView {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, ClickableView);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, WidgetAction)]
|
||||
pub enum ClickableViewAction {
|
||||
None,
|
||||
Click,
|
||||
}
|
||||
|
||||
impl Widget for ClickableView {
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
let uid = self.widget_uid();
|
||||
self.handle_event_with(cx, event, &mut |cx, action| {
|
||||
dispatch_action(cx, WidgetActionItem::new(action.into(), uid));
|
||||
});
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.view.redraw(cx);
|
||||
}
|
||||
|
||||
fn walk(&mut self, cx: &mut Cx) -> Walk {
|
||||
self.view.walk(cx)
|
||||
}
|
||||
|
||||
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
|
||||
self.view.find_widgets(path, cached, results);
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.view.draw_walk_widget(cx, walk)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClickableView {
|
||||
pub fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, ClickableViewAction),
|
||||
) {
|
||||
match event.hits(cx, self.view.area()){
|
||||
Hit::FingerDown(_fe) => {
|
||||
cx.set_key_focus(self.view.area());
|
||||
}
|
||||
Hit::FingerUp(fe) => if fe.was_tap() {
|
||||
dispatch_action(cx, ClickableViewAction::Click);
|
||||
}
|
||||
_ =>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, WidgetRef)]
|
||||
pub struct ClickableViewRef(WidgetRef);
|
||||
|
||||
impl ClickableViewRef {
|
||||
pub fn clicked(&self, actions: &WidgetActions) -> bool {
|
||||
if let Some(item) = actions.find_single_action(self.widget_uid()) {
|
||||
if let ClickableViewAction::Click = item.action() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
55
src/shared/custom_button.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::shared::styles::*;
|
||||
import crate::shared::clickable_view::ClickableView
|
||||
|
||||
CustomButton = <ClickableView> {
|
||||
width: Fill, height: Fit
|
||||
// flow: Right
|
||||
show_bg: false
|
||||
draw_bg: {
|
||||
// color: #EDEDED;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
button = <Button> {
|
||||
width: Fill, height: 34.
|
||||
text: "Welcome"
|
||||
draw_text: {
|
||||
text_style: <REGULAR_TEXT>{font_size: 12.},
|
||||
fn get_color(self) -> vec4 {
|
||||
return #000
|
||||
}
|
||||
}
|
||||
draw_bg: {
|
||||
border_radius: 8.
|
||||
fn pixel(self) -> vec4 {
|
||||
let border_color = #b4b4b4;
|
||||
let border_width = 0.5;
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
let body = mix(mix(#f, #f0, self.hover), #d1, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
389
src/shared/dropdown_menu.rs
Normal file
|
@ -0,0 +1,389 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use makepad_widgets::makepad_derive_widget::*;
|
||||
use makepad_widgets::makepad_draw::*;
|
||||
use makepad_widgets::widget::*;
|
||||
|
||||
use crate::shared::popup_menu::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import crate::shared::popup_menu::*;
|
||||
|
||||
DropDown = {{WechatDropDown}} {
|
||||
width: Fit,
|
||||
height: Fit,
|
||||
margin: {left: 1.0, right: 1.0, top: 1.0, bottom: 1.0},
|
||||
align: {x: 0., y: 0.},
|
||||
padding: {left: 5.0, top: 5.0, right: 4.0, bottom: 5.0}
|
||||
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
|
||||
draw_icon: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#9,
|
||||
#c,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
icon_walk: { width: 20., height: Fit}
|
||||
|
||||
popup_menu: <PopupMenu> {
|
||||
}
|
||||
|
||||
popup_shift: vec2(-6.0,4.0)
|
||||
|
||||
selected_item: -1
|
||||
animator: {
|
||||
hover = {
|
||||
default: off,
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.1}}
|
||||
apply: {
|
||||
draw_icon: {pressed: 0.0, hover: 0.0}
|
||||
}
|
||||
}
|
||||
|
||||
on = {
|
||||
from: {
|
||||
all: Forward {duration: 0.1}
|
||||
pressed: Forward {duration: 0.01}
|
||||
}
|
||||
apply: {
|
||||
draw_icon: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
}
|
||||
}
|
||||
|
||||
pressed = {
|
||||
from: {all: Forward {duration: 0.2}}
|
||||
apply: {
|
||||
draw_icon: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct WechatDropDown {
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
|
||||
#[live]
|
||||
draw_bg: DrawQuad,
|
||||
#[live]
|
||||
draw_icon: DrawIcon,
|
||||
#[live]
|
||||
icon_walk: Walk,
|
||||
|
||||
#[live]
|
||||
bind: String,
|
||||
#[live]
|
||||
bind_enum: String,
|
||||
|
||||
#[live]
|
||||
popup_menu: Option<LivePtr>,
|
||||
|
||||
#[live]
|
||||
labels: Vec<String>,
|
||||
#[live]
|
||||
values: Vec<LiveValue>,
|
||||
#[live]
|
||||
icons: Vec<LiveDependency>,
|
||||
|
||||
#[live]
|
||||
popup_shift: DVec2,
|
||||
|
||||
#[rust]
|
||||
is_open: bool,
|
||||
|
||||
#[live]
|
||||
selected_item: usize,
|
||||
}
|
||||
|
||||
impl LiveHook for WechatDropDown {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, WechatDropDown)
|
||||
}
|
||||
|
||||
fn after_apply(&mut self, cx: &mut Cx, from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
|
||||
if self.popup_menu.is_none() || !from.is_from_doc() {
|
||||
return;
|
||||
}
|
||||
let global = cx.global::<PopupMenuGlobal>().clone();
|
||||
let mut map = global.map.borrow_mut();
|
||||
|
||||
// when live styling clean up old style references
|
||||
map.retain(|k, _| cx.live_registry.borrow().generation_valid(*k));
|
||||
|
||||
let list_box = self.popup_menu.unwrap();
|
||||
map.get_or_insert(cx, list_box, |cx| {
|
||||
PopupMenu::new_from_ptr(cx, Some(list_box))
|
||||
});
|
||||
}
|
||||
}
|
||||
#[derive(Clone, WidgetAction, Debug)]
|
||||
pub enum WechatDropDownAction {
|
||||
Select(usize, LiveValue),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct PopupMenuGlobal {
|
||||
map: Rc<RefCell<ComponentMap<LivePtr, PopupMenu>>>,
|
||||
}
|
||||
|
||||
impl WechatDropDown {
|
||||
pub fn toggle_open(&mut self, cx: &mut Cx) {
|
||||
if self.is_open {
|
||||
self.set_closed(cx);
|
||||
} else {
|
||||
self.set_open(cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_open(&mut self, cx: &mut Cx) {
|
||||
self.is_open = true;
|
||||
self.draw_bg.redraw(cx);
|
||||
let global = cx.global::<PopupMenuGlobal>().clone();
|
||||
let mut map = global.map.borrow_mut();
|
||||
let lb = map.get_mut(&self.popup_menu.unwrap()).unwrap();
|
||||
let node_id = LiveId(self.selected_item as u64).into();
|
||||
lb.init_select_item(node_id);
|
||||
cx.sweep_lock(self.draw_bg.area());
|
||||
}
|
||||
|
||||
pub fn set_closed(&mut self, cx: &mut Cx) {
|
||||
self.is_open = false;
|
||||
self.draw_bg.redraw(cx);
|
||||
cx.sweep_unlock(self.draw_bg.area());
|
||||
}
|
||||
|
||||
pub fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WechatDropDownAction),
|
||||
) {
|
||||
self.animator_handle_event(cx, event);
|
||||
|
||||
if self.is_open && self.popup_menu.is_some() {
|
||||
let global = cx.global::<PopupMenuGlobal>().clone();
|
||||
let mut map = global.map.borrow_mut();
|
||||
let menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
|
||||
let mut close = false;
|
||||
menu.handle_event_with(
|
||||
cx,
|
||||
event,
|
||||
self.draw_bg.area(),
|
||||
&mut |cx, action| if let PopupMenuAction::WasSelected(node_id) = action {
|
||||
self.selected_item = node_id.0 .0 as usize;
|
||||
dispatch_action(
|
||||
cx,
|
||||
WechatDropDownAction::Select(
|
||||
self.selected_item,
|
||||
self.values
|
||||
.get(self.selected_item)
|
||||
.cloned()
|
||||
.unwrap_or(LiveValue::None),
|
||||
),
|
||||
);
|
||||
self.draw_bg.redraw(cx);
|
||||
close = true;
|
||||
},
|
||||
);
|
||||
if close {
|
||||
self.set_closed(cx);
|
||||
}
|
||||
|
||||
// check if we touch outside of the popup menu
|
||||
if let Event::MouseDown(e) = event {
|
||||
if !menu.menu_contains_pos(cx, e.abs) {
|
||||
self.set_closed(cx);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: close on clicking outside of the popup menu
|
||||
match event.hits_with_sweep_area(cx, self.draw_bg.area(), self.draw_bg.area()) {
|
||||
Hit::KeyFocusLost(_) => {
|
||||
self.set_closed(cx);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
self.draw_bg.redraw(cx);
|
||||
}
|
||||
Hit::KeyDown(ke) => match ke.key_code {
|
||||
KeyCode::ArrowUp => {
|
||||
if self.selected_item > 0 {
|
||||
self.selected_item -= 1;
|
||||
dispatch_action(
|
||||
cx,
|
||||
WechatDropDownAction::Select(
|
||||
self.selected_item,
|
||||
self.values[self.selected_item].clone(),
|
||||
),
|
||||
);
|
||||
self.set_closed(cx);
|
||||
self.draw_bg.redraw(cx);
|
||||
}
|
||||
}
|
||||
KeyCode::ArrowDown => {
|
||||
if !self.values.is_empty() && self.selected_item < self.values.len() - 1 {
|
||||
self.selected_item += 1;
|
||||
dispatch_action(
|
||||
cx,
|
||||
WechatDropDownAction::Select(
|
||||
self.selected_item,
|
||||
self.values[self.selected_item].clone(),
|
||||
),
|
||||
);
|
||||
self.set_closed(cx);
|
||||
self.draw_bg.redraw(cx);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Hit::FingerDown(_fe) => {
|
||||
cx.set_key_focus(self.draw_bg.area());
|
||||
self.toggle_open(cx);
|
||||
self.animator_play(cx, id!(hover.pressed));
|
||||
}
|
||||
Hit::FingerHoverIn(_) => {
|
||||
cx.set_cursor(MouseCursor::Hand);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerUp(fe) => {
|
||||
if fe.is_over {
|
||||
if fe.device.has_hovers() {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
|
||||
// cx.clear_sweep_lock(self.draw_bg.area());
|
||||
|
||||
self.draw_bg.begin(cx, walk, self.layout);
|
||||
//let start_pos = cx.turtle().rect().pos;
|
||||
self.draw_icon.draw_walk(cx, self.icon_walk);
|
||||
self.draw_bg.end(cx);
|
||||
|
||||
cx.add_nav_stop(self.draw_bg.area(), NavRole::DropDown, Margin::default());
|
||||
|
||||
if self.is_open && self.popup_menu.is_some() {
|
||||
// cx.set_sweep_lock(self.draw_bg.area());
|
||||
let global = cx.global::<PopupMenuGlobal>().clone();
|
||||
let mut map = global.map.borrow_mut();
|
||||
let popup_menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
|
||||
|
||||
popup_menu.begin(cx);
|
||||
|
||||
for (i, item) in self.labels.iter().enumerate() {
|
||||
let node_id = LiveId(i as u64).into();
|
||||
popup_menu.draw_item(cx, node_id, item, self.icons[i].clone());
|
||||
}
|
||||
|
||||
popup_menu.end(cx, self.draw_bg.area());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It is named WechatDropDown because DropDown is already a widget in makepad_widgets
|
||||
impl Widget for WechatDropDown {
|
||||
fn widget_to_data(
|
||||
&self,
|
||||
_cx: &mut Cx,
|
||||
actions: &WidgetActions,
|
||||
nodes: &mut LiveNodeVec,
|
||||
path: &[LiveId],
|
||||
) -> bool {
|
||||
match actions.single_action(self.widget_uid()) {
|
||||
WechatDropDownAction::Select(_, value) => {
|
||||
nodes.write_field_value(path, value.clone());
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
|
||||
if let Some(value) = nodes.read_field_value(path) {
|
||||
if let Some(index) = self.values.iter().position(|v| v == value) {
|
||||
if self.selected_item != index {
|
||||
self.selected_item = index;
|
||||
self.redraw(cx);
|
||||
}
|
||||
} else {
|
||||
// error!("Value not in values list {:?}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.draw_bg.redraw(cx);
|
||||
}
|
||||
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
let uid = self.widget_uid();
|
||||
self.handle_event_with(cx, event, &mut |cx, action| {
|
||||
dispatch_action(cx, WidgetActionItem::new(action.into(), uid))
|
||||
});
|
||||
}
|
||||
|
||||
fn walk(&mut self, _cx: &mut Cx) -> Walk {
|
||||
self.walk
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.draw_walk(cx, walk);
|
||||
WidgetDraw::done()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, WidgetRef)]
|
||||
pub struct WechatDropDownRef(WidgetRef);
|
||||
|
||||
impl WechatDropDownRef {
|
||||
pub fn item_clicked(&mut self, item_id: &[LiveId], actions: &WidgetActions) -> bool {
|
||||
if let Some(item) = actions.find_single_action(self.widget_uid()) {
|
||||
if let WechatDropDownAction::Select(_id, value) = item.action() {
|
||||
return LiveValue::Bool(true) == value.enum_eq(item_id)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
223
src/shared/header.rs
Normal file
|
@ -0,0 +1,223 @@
|
|||
use makepad_widgets::widget::WidgetCache;
|
||||
use makepad_widgets::*;
|
||||
use crate::shared::stack_view_action::StackViewAction;
|
||||
use crate::shared::dropdown_menu::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::shared::styles::*;
|
||||
import crate::shared::helpers::FillerX;
|
||||
import crate::shared::dropdown_menu::DropDown;
|
||||
|
||||
SimpleHeaderContent = <View> {
|
||||
width: Fill, height: Fit
|
||||
flow: Right, align: {x: 0.5, y: 0.5}
|
||||
|
||||
<FillerX> {}
|
||||
|
||||
title_container = <View> {
|
||||
width: Fill, height: Fit
|
||||
align: {x: 0.5, y: 0.5}
|
||||
|
||||
title = <Label> {
|
||||
width: Fit, height: Fit
|
||||
draw_text: {
|
||||
color: #fff,
|
||||
text_style: <TITLE_TEXT>{},
|
||||
},
|
||||
text: "微信"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimpleHeader = <View> {
|
||||
width: Fill , height: Fit, margin: 0
|
||||
padding: {bottom: 7., top: 50.}, align: {x: 0.5, y: 0.0}, spacing: 0.0, flow: Overlay
|
||||
show_bg: true
|
||||
draw_bg: {
|
||||
color: #EDEDED
|
||||
}
|
||||
|
||||
content = <SimpleHeaderContent> {}
|
||||
}
|
||||
|
||||
HeaderWithLeftActionButton = <SimpleHeader> {
|
||||
content = {
|
||||
flow: Overlay
|
||||
|
||||
button_container = <View> {
|
||||
left_button = <Button> {
|
||||
width: Fit, height: 68
|
||||
icon_walk: {width: 20, height: 68}
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
draw_icon: {
|
||||
color: #000;
|
||||
brightness: 0.8;
|
||||
}
|
||||
}
|
||||
divider = <View> {
|
||||
width: Fill, height: Fit
|
||||
right_button = <Button> {
|
||||
width: Fit, height: 68
|
||||
icon_walk: {width: 20, height: 68}
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
draw_icon: {
|
||||
color: #000;
|
||||
brightness: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeaderWithRightActionButton = <SimpleHeader> {
|
||||
content = {
|
||||
flow: Overlay
|
||||
|
||||
button_container = <View> {
|
||||
spacer = <View> {
|
||||
width: Fill, height: Fit
|
||||
right_button = <Button> {
|
||||
width: Fit, height: 68
|
||||
icon_walk: {width: 20, height: 68}
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
draw_icon: {
|
||||
color: #000;
|
||||
brightness: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeaderDropDownMenu = {{HeaderDropDownMenu}} {
|
||||
width: Fill, height: Fit, margin: 0
|
||||
padding: {bottom: 7., top: 50.}, align: {x: 0.5, y: 0.0}, spacing: 0.0, flow: Overlay
|
||||
show_bg: true
|
||||
draw_bg: {
|
||||
color: #EDEDED
|
||||
}
|
||||
|
||||
content = <SimpleHeaderContent> {
|
||||
width: Fill, height: Fit
|
||||
flow: Right, align: {x: 0.5, y: 0.5}
|
||||
|
||||
button_container = <View> {
|
||||
width: Fill, height: Fit
|
||||
align: {x: 1.0, y: 0.5}, flow: Right, spacing: 5., padding: {right: 5.}
|
||||
|
||||
// TODO: this should be the searchbar, and we need consistent svgs
|
||||
left_button = <Button> {
|
||||
width: Fit, height: Fit
|
||||
padding: 0.
|
||||
icon_walk: {width: 20, height: Fit}
|
||||
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/icons/search.svg")
|
||||
color: #000;
|
||||
brightness: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
menu = <DropDown> {
|
||||
height: Fit, width: Fit
|
||||
draw_icon: {
|
||||
svg_file: dep("crate://self/resources/icons/menu.svg")
|
||||
color: #000;
|
||||
brightness: 0.8;
|
||||
}
|
||||
labels: ["Add Contact", "New Chat", "Scan", "Money"]
|
||||
values: [AddContact, NewChat, Scan, Money]
|
||||
icons: [
|
||||
dep("crate://self/resources/icons/add_contact.svg"),
|
||||
dep("crate://self/resources/icons/chat.svg"),
|
||||
dep("crate://self/resources/icons/scan.svg"),
|
||||
dep("crate://self/resources/icons/money.svg")
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct HeaderDropDownMenu {
|
||||
#[deref]
|
||||
view: View,
|
||||
}
|
||||
|
||||
impl LiveHook for HeaderDropDownMenu {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, HeaderDropDownMenu);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for HeaderDropDownMenu {
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
let uid = self.widget_uid();
|
||||
self.handle_event_with(cx, event, &mut |cx, action: StackViewAction| {
|
||||
dispatch_action(cx, WidgetActionItem::new(action.into(), uid));
|
||||
});
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.view.redraw(cx);
|
||||
}
|
||||
|
||||
fn walk(&mut self, cx: &mut Cx) -> Walk {
|
||||
self.view.walk(cx)
|
||||
}
|
||||
|
||||
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
|
||||
self.view.find_widgets(path, cached, results);
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.view.draw_walk_widget(cx, walk)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderDropDownMenu {
|
||||
pub fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, StackViewAction),
|
||||
) {
|
||||
let actions = self.view.handle_widget_event(cx, event);
|
||||
|
||||
if self.wechat_drop_down(id!(menu)).item_clicked(id!(AddContact), &actions) {
|
||||
dispatch_action(cx, StackViewAction::ShowAddContact);
|
||||
}
|
||||
}
|
||||
}
|
27
src/shared/helpers.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
Divider = <View> {
|
||||
width: Fill, height: Fit
|
||||
flow: Down
|
||||
<RoundedView> {
|
||||
width: Fill,
|
||||
height: 1.,
|
||||
draw_bg: {color: (#ddd)}
|
||||
}
|
||||
}
|
||||
|
||||
LineH = <RoundedView> {
|
||||
width: Fill,
|
||||
height: 2,
|
||||
margin: 0.0,
|
||||
padding: 0.0, spacing: 0.0
|
||||
draw_bg: {color: (COLOR_DIVIDER)}
|
||||
}
|
||||
|
||||
FillerX = <View> { width: Fill, height: Fit }
|
||||
FillerY = <View> { width: Fit, height: Fill }
|
||||
}
|
10
src/shared/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod stack_navigation;
|
||||
pub mod clickable_view;
|
||||
pub mod stack_view_action;
|
||||
pub mod dropdown_menu;
|
||||
pub mod popup_menu;
|
||||
pub mod header;
|
||||
pub mod search_bar;
|
||||
pub mod custom_button;
|
||||
pub mod helpers;
|
||||
pub mod styles;
|
446
src/shared/popup_menu.rs
Normal file
|
@ -0,0 +1,446 @@
|
|||
use makepad_widgets::makepad_derive_widget::*;
|
||||
use makepad_widgets::makepad_draw::*;
|
||||
use makepad_widgets::widget::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import crate::shared::styles::*;
|
||||
|
||||
DrawBg = {{DrawBg}} {
|
||||
instance color: #4
|
||||
instance color_selected: #5
|
||||
instance border_radius: 4.0
|
||||
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
|
||||
sdf.box(
|
||||
1.0,
|
||||
1.0,
|
||||
self.rect_size.x,
|
||||
self.rect_size.y,
|
||||
self.border_radius
|
||||
)
|
||||
sdf.fill_keep(mix(self.color, self.color_selected, self.hover))
|
||||
return sdf.result;
|
||||
}
|
||||
}
|
||||
|
||||
DrawName = {{DrawName}} {
|
||||
text_style: <REGULAR_TEXT>{font_size: 9},
|
||||
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
THEME_COLOR_TEXT_DEFAULT,
|
||||
THEME_COLOR_TEXT_SELECTED,
|
||||
self.selected
|
||||
),
|
||||
THEME_COLOR_TEXT_HOVER,
|
||||
self.hover
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem = {{MenuItem}} {
|
||||
align: {y: 0.5},
|
||||
padding: {left: 5., top: 10., bottom: 10., right: 5.},
|
||||
spacing: 5.,
|
||||
width: Fill,
|
||||
height: Fit
|
||||
|
||||
icon_walk: {width: 15., height: Fit, margin: {bottom: 3.}}
|
||||
draw_icon: {
|
||||
color: #f2f2f2;
|
||||
brightness: 0.8;
|
||||
}
|
||||
|
||||
animator: {
|
||||
hover = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
draw_bg: {hover: 0.0}
|
||||
draw_name: {hover: 0.0}
|
||||
}
|
||||
}
|
||||
on = {
|
||||
cursor: Hand
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
draw_bg: {hover: 1.0}
|
||||
draw_name: {hover: 1.0}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
draw_bg: {selected: 0.0,}
|
||||
draw_name: {selected: 0.0,}
|
||||
}
|
||||
}
|
||||
on = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
draw_bg: {selected: 1.0,}
|
||||
draw_name: {selected: 1.0,}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
indent_width: 10.0
|
||||
}
|
||||
|
||||
PopupMenu = {{PopupMenu}} {
|
||||
menu_item: <MenuItem> {}
|
||||
flow: Down,
|
||||
padding: 5.,
|
||||
width: 140.,
|
||||
height: Fit,
|
||||
|
||||
icon_walk: {width: 20., height: Fit}
|
||||
draw_icon:{
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#9,
|
||||
#c,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_bg: {
|
||||
instance color: #4
|
||||
instance border_width: 0.0,
|
||||
instance border_color: #4,
|
||||
instance border_radius: 4.0
|
||||
|
||||
fn get_color(self) -> vec4 {
|
||||
return self.color
|
||||
}
|
||||
|
||||
fn get_border_color(self) -> vec4 {
|
||||
return self.border_color
|
||||
}
|
||||
|
||||
fn pixel(self) -> vec4 {
|
||||
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
|
||||
// sdf.blur = 18.0;
|
||||
sdf.box(
|
||||
self.border_width,
|
||||
self.border_width,
|
||||
self.rect_size.x - (self.border_width * 2.0),
|
||||
self.rect_size.y - (self.border_width * 2.0),
|
||||
self.border_radius
|
||||
)
|
||||
sdf.fill_keep(self.get_color())
|
||||
return sdf.result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook)]
|
||||
#[repr(C)]
|
||||
struct DrawBg {
|
||||
#[deref]
|
||||
draw_super: DrawQuad,
|
||||
#[live]
|
||||
selected: f32,
|
||||
#[live]
|
||||
hover: f32,
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook)]
|
||||
#[repr(C)]
|
||||
struct DrawName {
|
||||
#[deref]
|
||||
draw_super: DrawText,
|
||||
#[live]
|
||||
selected: f32,
|
||||
#[live]
|
||||
hover: f32,
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct PopupMenu {
|
||||
#[live]
|
||||
draw_list: DrawList2d,
|
||||
|
||||
#[live]
|
||||
menu_item: Option<LivePtr>,
|
||||
|
||||
#[live]
|
||||
draw_bg: DrawQuad,
|
||||
#[live]
|
||||
draw_icon: DrawIcon,
|
||||
#[live]
|
||||
icon_walk: Walk,
|
||||
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
|
||||
#[live]
|
||||
labels: Vec<String>,
|
||||
#[live]
|
||||
values: Vec<LiveValue>,
|
||||
|
||||
#[live]
|
||||
items: Vec<String>,
|
||||
#[rust]
|
||||
menu_items: ComponentMap<MenuItemId, MenuItem>,
|
||||
#[rust]
|
||||
init_select_item: Option<MenuItemId>,
|
||||
|
||||
#[rust]
|
||||
first_tap: bool,
|
||||
#[rust]
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl LiveHook for PopupMenu {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, PopupMenu)
|
||||
}
|
||||
|
||||
fn after_apply(&mut self, cx: &mut Cx, from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
|
||||
if let Some(index) = nodes.child_by_name(index, live_id!(list_node).as_field()) {
|
||||
for (_, node) in self.menu_items.iter_mut() {
|
||||
node.apply(cx, from, index, nodes);
|
||||
}
|
||||
}
|
||||
self.draw_list.redraw(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for PopupMenu {
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
_cx: &mut Cx,
|
||||
_event: &Event,
|
||||
_dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.draw_bg.begin(cx, walk, self.layout);
|
||||
self.draw_icon.draw_walk(cx, self.icon_walk);
|
||||
self.draw_bg.end(cx);
|
||||
WidgetDraw::done()
|
||||
}
|
||||
|
||||
fn walk(&mut self, _cx: &mut Cx) -> Walk {
|
||||
self.walk
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.draw_bg.redraw(cx);
|
||||
self.draw_icon.redraw(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl PopupMenu {
|
||||
pub fn menu_contains_pos(&self, cx: &mut Cx, pos: DVec2) -> bool {
|
||||
self.draw_bg.area().get_clipped_rect(cx).contains(pos)
|
||||
}
|
||||
|
||||
pub fn begin(&mut self, cx: &mut Cx2d) {
|
||||
self.draw_list.begin_overlay_reuse(cx);
|
||||
|
||||
cx.begin_pass_sized_turtle(Layout::flow_down());
|
||||
|
||||
self.draw_bg.begin(cx, self.walk, self.layout);
|
||||
self.count = 0;
|
||||
}
|
||||
|
||||
pub fn end(&mut self, cx: &mut Cx2d, shift_area: Area) {
|
||||
self.draw_bg.end(cx);
|
||||
let area = self.draw_bg.area().get_rect(cx);
|
||||
let shift = DVec2 {
|
||||
x: -area.size.x + (shift_area.get_rect(cx).size.x * 0.7),
|
||||
y: 30.,
|
||||
};
|
||||
|
||||
cx.end_pass_sized_turtle_with_shift(shift_area, shift);
|
||||
self.draw_list.end(cx);
|
||||
|
||||
self.menu_items.retain_visible();
|
||||
if let Some(init_select_item) = self.init_select_item.take() {
|
||||
self.select_item_state(cx, init_select_item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_item(
|
||||
&mut self,
|
||||
cx: &mut Cx2d,
|
||||
item_id: MenuItemId,
|
||||
label: &str,
|
||||
icon: LiveDependency,
|
||||
) {
|
||||
self.count += 1;
|
||||
|
||||
let menu_item = self.menu_item;
|
||||
let menu_item = self
|
||||
.menu_items
|
||||
.get_or_insert(cx, item_id, |cx| MenuItem::new_from_ptr(cx, menu_item));
|
||||
menu_item.draw_item(cx, label, icon);
|
||||
}
|
||||
|
||||
pub fn init_select_item(&mut self, which_id: MenuItemId) {
|
||||
self.init_select_item = Some(which_id);
|
||||
self.first_tap = true;
|
||||
}
|
||||
|
||||
fn select_item_state(&mut self, cx: &mut Cx, which_id: MenuItemId) {
|
||||
for (id, item) in &mut *self.menu_items {
|
||||
if *id == which_id {
|
||||
item.animator_cut(cx, id!(select.on));
|
||||
item.animator_cut(cx, id!(hover.on));
|
||||
} else {
|
||||
item.animator_cut(cx, id!(select.off));
|
||||
item.animator_cut(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
sweep_area: Area,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, PopupMenuAction),
|
||||
) {
|
||||
let mut actions = Vec::new();
|
||||
for (item_id, node) in self.menu_items.iter_mut() {
|
||||
node.handle_event_with(cx, event, sweep_area, &mut |_, e| {
|
||||
actions.push((*item_id, e))
|
||||
});
|
||||
}
|
||||
|
||||
for (node_id, action) in actions {
|
||||
match action {
|
||||
MenuItemAction::WasSweeped => {
|
||||
self.select_item_state(cx, node_id);
|
||||
dispatch_action(cx, PopupMenuAction::WasSweeped(node_id));
|
||||
}
|
||||
MenuItemAction::WasSelected => {
|
||||
self.select_item_state(cx, node_id);
|
||||
dispatch_action(cx, PopupMenuAction::WasSelected(node_id));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook)]
|
||||
pub struct MenuItem {
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
|
||||
#[live]
|
||||
draw_bg: DrawBg,
|
||||
#[live]
|
||||
draw_name: DrawName,
|
||||
|
||||
#[live]
|
||||
draw_icon: DrawIcon,
|
||||
#[live]
|
||||
icon_walk: Walk,
|
||||
|
||||
#[live]
|
||||
indent_width: f32,
|
||||
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
#[live]
|
||||
opened: f32,
|
||||
#[live]
|
||||
hover: f32,
|
||||
#[live]
|
||||
selected: f32,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub enum MenuItemAction {
|
||||
WasSweeped,
|
||||
WasSelected,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, WidgetAction, Debug)]
|
||||
pub enum PopupMenuAction {
|
||||
WasSweeped(MenuItemId),
|
||||
WasSelected(MenuItemId),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
|
||||
pub struct MenuItemId(pub LiveId);
|
||||
|
||||
impl MenuItem {
|
||||
pub fn draw_item(&mut self, cx: &mut Cx2d, label: &str, icon: LiveDependency) {
|
||||
self.draw_bg.begin(cx, self.walk, self.layout);
|
||||
self.draw_icon.svg_file = icon;
|
||||
self.draw_icon.draw_walk(cx, self.icon_walk);
|
||||
self.draw_name
|
||||
.draw_walk(cx, Walk::fit(), Align { x: 0., y: 0.5 }, label);
|
||||
self.draw_bg.end(cx);
|
||||
}
|
||||
|
||||
pub fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
sweep_area: Area,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, MenuItemAction),
|
||||
) {
|
||||
if self.animator_handle_event(cx, event).must_redraw() {
|
||||
self.draw_bg.area().redraw(cx);
|
||||
}
|
||||
|
||||
match event.hits_with_options(
|
||||
cx,
|
||||
self.draw_bg.area(),
|
||||
HitOptions::new().with_sweep_area(sweep_area),
|
||||
) {
|
||||
Hit::FingerHoverIn(_) => {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerDown(_) => {
|
||||
dispatch_action(cx, MenuItemAction::WasSweeped);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
self.animator_play(cx, id!(select.on));
|
||||
}
|
||||
Hit::FingerUp(se) => {
|
||||
if !se.is_sweep {
|
||||
dispatch_action(cx, MenuItemAction::WasSelected);
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
self.animator_play(cx, id!(select.off));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
72
src/shared/search_bar.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::shared::styles::*;
|
||||
|
||||
SearchBar = <View> {
|
||||
width: Fill, height: Fit
|
||||
show_bg: true
|
||||
draw_bg: {
|
||||
color: #EDEDED;
|
||||
}
|
||||
|
||||
input = <TextInput> {
|
||||
width: Fill, height: Fit, margin: {left: 5.0, right: 5.0, top: 5.0, bottom: 15.0}
|
||||
clip_x: true,
|
||||
clip_y: true,
|
||||
align: {y: 0.5}
|
||||
empty_message: "Search"
|
||||
draw_bg: {
|
||||
color: #fff
|
||||
}
|
||||
draw_text: {
|
||||
text_style:<REGULAR_TEXT>{},
|
||||
|
||||
fn get_color(self) -> vec4 {
|
||||
return #ccc
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find a way to override colors
|
||||
draw_cursor: {
|
||||
instance focus: 0.0
|
||||
uniform border_radius: 0.5
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
sdf.box(
|
||||
0.,
|
||||
0.,
|
||||
self.rect_size.x,
|
||||
self.rect_size.y,
|
||||
self.border_radius
|
||||
)
|
||||
sdf.fill(mix(#0f0, #0b0, self.focus));
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find a way to override colors
|
||||
draw_select: {
|
||||
instance hover: 0.0
|
||||
instance focus: 0.0
|
||||
uniform border_radius: 2.0
|
||||
fn pixel(self) -> vec4 {
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
|
||||
sdf.box(
|
||||
0.,
|
||||
0.,
|
||||
self.rect_size.x,
|
||||
self.rect_size.y,
|
||||
self.border_radius
|
||||
)
|
||||
sdf.fill(mix(#0e0, #0d0, self.focus)); // Pad color
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
304
src/shared/stack_navigation.rs
Normal file
|
@ -0,0 +1,304 @@
|
|||
use makepad_widgets::widget::WidgetCache;
|
||||
use makepad_widgets::*;
|
||||
use crate::shared::stack_view_action::StackViewAction;
|
||||
use std::collections::HashMap;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import crate::shared::header::HeaderWithLeftActionButton;
|
||||
|
||||
Header = <HeaderWithLeftActionButton> {
|
||||
content = {
|
||||
title_container = {
|
||||
title = {
|
||||
text: "My Stack View"
|
||||
}
|
||||
}
|
||||
|
||||
button_container = {
|
||||
left_button = {
|
||||
width: Fit
|
||||
icon_walk: {width: 10}
|
||||
draw_icon: {
|
||||
svg_file: dep("crate://self/resources/icons/back.svg")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StackNavigationView = {{StackNavigationView}} {
|
||||
visible: false
|
||||
width: Fill, height: Fill
|
||||
flow: Down
|
||||
show_bg: true
|
||||
draw_bg: {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
header = <Header> {}
|
||||
|
||||
// TBD Adjust this based on actual screen size
|
||||
offset: 2.0
|
||||
|
||||
animator: {
|
||||
slide = {
|
||||
default: hide,
|
||||
hide = {
|
||||
ease: ExpDecay {d1: 0.80, d2: 0.97}
|
||||
from: {all: Forward {duration: 0.3}}
|
||||
// Bug: Constants are not working as part of an live state value
|
||||
apply: {offset: 400.0}
|
||||
}
|
||||
|
||||
show = {
|
||||
ease: ExpDecay {d1: 0.82, d2: 0.95}
|
||||
from: {all: Forward {duration: 0.3}}
|
||||
apply: {offset: 0.0}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StackNavigation = {{StackNavigation}} {
|
||||
width: Fill, height: Fill
|
||||
flow: Overlay
|
||||
|
||||
root_view = <View> {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct StackNavigationView {
|
||||
#[deref]
|
||||
view:View,
|
||||
|
||||
#[live]
|
||||
offset: f64,
|
||||
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
}
|
||||
|
||||
impl LiveHook for StackNavigationView {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, StackNavigationView);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for StackNavigationView {
|
||||
fn walk(&mut self, cx: &mut Cx) -> Walk {
|
||||
self.view.walk(cx)
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.view.redraw(cx)
|
||||
}
|
||||
|
||||
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
|
||||
self.view.find_widgets(path, cached, results);
|
||||
}
|
||||
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
self.handle_event_with(cx, event, dispatch_action);
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.view.draw_walk_widget(
|
||||
cx,
|
||||
walk.with_abs_pos(DVec2 {
|
||||
x: self.offset,
|
||||
y: 0.,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl StackNavigationView {
|
||||
pub fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
if self.animator_handle_event(cx, event).is_animating() {
|
||||
self.view.redraw(cx);
|
||||
}
|
||||
|
||||
let actions = self.view.handle_widget_event(cx, event);
|
||||
if self.button(id!(left_button)).clicked(&actions) {
|
||||
self.animator_play(cx, id!(slide.hide));
|
||||
}
|
||||
|
||||
for action in actions.into_iter() {
|
||||
dispatch_action(cx, action);
|
||||
}
|
||||
|
||||
if self.animator.animator_in_state(cx, id!(slide.hide))
|
||||
&& !self.animator.is_track_animating(cx, id!(slide))
|
||||
{
|
||||
self.apply_over(cx, live! {visible: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, WidgetRef)]
|
||||
pub struct StackNavigationViewRef(pub WidgetRef);
|
||||
|
||||
impl StackNavigationViewRef {
|
||||
pub fn show(&mut self, cx: &mut Cx) {
|
||||
if let Some(mut inner) = self.borrow_mut() {
|
||||
inner.apply_over(cx, live! {visible: true});
|
||||
inner.animator_play(cx, id!(slide.show));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_showing(&self, cx: &mut Cx) -> bool {
|
||||
if let Some(inner) = self.borrow() {
|
||||
inner.animator.animator_in_state(cx, id!(slide.show))
|
||||
|| inner.animator.is_track_animating(cx, id!(slide))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_animating(&self, cx: &mut Cx) -> bool {
|
||||
if let Some(inner) = self.borrow() {
|
||||
inner.animator.is_track_animating(cx, id!(slide))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ActiveStackView {
|
||||
#[default]
|
||||
None,
|
||||
Active(LiveId),
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct StackNavigation {
|
||||
#[deref]
|
||||
view: View,
|
||||
#[rust]
|
||||
active_stack_view: ActiveStackView,
|
||||
}
|
||||
|
||||
impl LiveHook for StackNavigation {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, StackNavigation);
|
||||
}
|
||||
|
||||
fn after_new_from_doc(&mut self, _cx: &mut Cx) {
|
||||
self.active_stack_view = ActiveStackView::None;
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for StackNavigation {
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
let mut actions = vec![];
|
||||
|
||||
for widget_ref in self.get_active_views(cx).iter() {
|
||||
for a in widget_ref.handle_widget_event(cx, event) {
|
||||
actions.push(a);
|
||||
}
|
||||
}
|
||||
|
||||
for action in actions.into_iter() {
|
||||
dispatch_action(cx, action);
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
for widget_ref in self.get_active_views(cx).iter() {
|
||||
widget_ref.redraw(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
|
||||
// We're only usingView widget ability to find widgets
|
||||
self.view.find_widgets(path, cached, results);
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
for widget_ref in self.get_active_views(cx.cx).iter() {
|
||||
widget_ref.draw_walk_widget(cx, walk) ?;
|
||||
}
|
||||
WidgetDraw::done()
|
||||
}
|
||||
}
|
||||
|
||||
impl StackNavigation {
|
||||
pub fn show_stack_view_by_id(&mut self, stack_view_id: LiveId, cx: &mut Cx) {
|
||||
if let ActiveStackView::None = self.active_stack_view {
|
||||
let mut stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
|
||||
stack_view_ref.show(cx);
|
||||
self.active_stack_view = ActiveStackView::Active(stack_view_id);
|
||||
self.redraw(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_active_views(&mut self, cx: &mut Cx) -> Vec<WidgetRef> {
|
||||
match self.active_stack_view {
|
||||
ActiveStackView::None => {
|
||||
vec![self.view.widget(id!(root_view))]
|
||||
},
|
||||
ActiveStackView::Active(stack_view_id) => {
|
||||
let stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
|
||||
let mut views = vec![];
|
||||
|
||||
if stack_view_ref.is_showing(cx) {
|
||||
if stack_view_ref.is_animating(cx) {
|
||||
views.push(self.view.widget(id!(root_view)));
|
||||
}
|
||||
views.push(stack_view_ref.0.clone());
|
||||
views
|
||||
} else {
|
||||
self.active_stack_view = ActiveStackView::None;
|
||||
vec![self.view.widget(id!(root_view))]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, WidgetRef, Debug)]
|
||||
pub struct StackNavigationRef(pub WidgetRef);
|
||||
|
||||
impl StackNavigationRef {
|
||||
pub fn show_stack_view_by_id(&mut self, stack_view_id: LiveId, cx: &mut Cx) {
|
||||
if let Some(mut inner) = self.borrow_mut() {
|
||||
inner.show_stack_view_by_id(stack_view_id, cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_stack_view_actions(&mut self, cx: &mut Cx, actions: &WidgetActions, destinations: &HashMap<StackViewAction, LiveId>) {
|
||||
for action in actions {
|
||||
let stack_view_action = action.action();
|
||||
if let Some(stack_view_id) = destinations.get(&stack_view_action) {
|
||||
self.show_stack_view_by_id(*stack_view_id, cx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_title(&self, stack_view_id: LiveId, title: &str) {
|
||||
if let Some(mut inner) = self.borrow_mut() {
|
||||
let stack_view_ref = inner.stack_navigation_view(&[stack_view_id]);
|
||||
stack_view_ref.label(id!(title)).set_text(title);
|
||||
}
|
||||
}
|
||||
}
|
11
src/shared/stack_view_action.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
#[derive(Clone, WidgetAction, Eq, Hash, PartialEq)]
|
||||
pub enum StackViewAction {
|
||||
None,
|
||||
ShowAddContact,
|
||||
ShowMoments,
|
||||
ShowMyProfile,
|
||||
ShowCounterScreen,
|
||||
ShowChat,
|
||||
}
|
30
src/shared/styles.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
TITLE_TEXT = {
|
||||
font_size: (14),
|
||||
// font: {path: dep("crate://makepad-widgets/resources/GoNotoKurrent-Regular.ttf")}
|
||||
}
|
||||
|
||||
REGULAR_TEXT = {
|
||||
font_size: (12),
|
||||
font: {path: dep("crate://makepad-widgets/resources/GoNotoKurrent-Regular.ttf")}
|
||||
}
|
||||
|
||||
TEXT_SUB = {
|
||||
font_size: (FONT_SIZE_SUB),
|
||||
font: {path: dep("crate://makepad-widgets/resources/GoNotoKurrent-Regular.ttf")}
|
||||
}
|
||||
H3_TEXT_REGULAR = {
|
||||
font_size: 9.0,
|
||||
font: {path: dep("crate://makepad-widgets/resources/IBMPlexSans-Text.ttf")}
|
||||
}
|
||||
|
||||
LOGO_MAKEPAD = dep("crate://self/resources/makepad.png")
|
||||
|
||||
CARD_CORNER_RADIUS = 20.
|
||||
CARD_HEIGHT = 550
|
||||
COLOR_PROFILE_CIRCLE = #xfff8ee
|
||||
COLOR_DIVIDER = #x00000018
|
||||
COLOR_DIVIDER_DARK = #x00000044
|
||||
}
|
20
src/welcome/home_screen.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::shared::header::HeaderDropDownMenu;
|
||||
import crate::welcome::welcome::WelcomeScreen;
|
||||
|
||||
HomeScreen = <View> {
|
||||
width: Fill, height: Fill
|
||||
flow: Down
|
||||
show_bg: true,
|
||||
draw_bg: {
|
||||
color: #fff
|
||||
}
|
||||
<HeaderDropDownMenu> {}
|
||||
<ChatList> {}
|
||||
}
|
||||
}
|
1
src/welcome/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod welcome_screen;
|
166
src/welcome/welcome_screen.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
use crate::shared::clickable_view::*;
|
||||
use crate::shared::stack_view_action::StackViewAction;
|
||||
use makepad_widgets::widget::WidgetCache;
|
||||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_draw::shader::std::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
|
||||
import crate::shared::helpers::*;
|
||||
import crate::shared::helpers::Divider;
|
||||
import crate::shared::styles::*;
|
||||
import crate::shared::clickable_view::ClickableView
|
||||
import crate::shared::search_bar::SearchBar;
|
||||
import crate::shared::custom_button::CustomButton;
|
||||
|
||||
IMG_DEFAULT_AVATAR = dep("crate://self/resources/img/default_avatar.png")
|
||||
IMG_FAVORITES = dep("crate://self/resources/img/favorites.png")
|
||||
IMG_MY_POSTS = dep("crate://self/resources/img/my-posts.png")
|
||||
IMG_STICKER_GALLERY = dep("crate://self/resources/img/sticker-gallery.png")
|
||||
IMG_SETTINGS = dep("crate://self/resources/img/settings.png")
|
||||
IMG_QR = dep("crate://self/resources/img/qr_icon.png")
|
||||
|
||||
Welcome = {{Welcome}} {
|
||||
view: {
|
||||
width: Fill, height: Fit
|
||||
// flow: Down,
|
||||
spacing: 10.
|
||||
// show_bg: true,
|
||||
draw_bg: {
|
||||
color: #000
|
||||
}
|
||||
WelcomeInfo = <View> {
|
||||
width: Fill, height: Fill
|
||||
flow: Right,
|
||||
spacing: 10.,
|
||||
// padding: {top: 100., bottom: 100., right: 10., left: 10.}
|
||||
align: {
|
||||
x: 0.5,
|
||||
y: 0.5
|
||||
},
|
||||
show_bg: true
|
||||
draw_bg: {
|
||||
color: #fff
|
||||
}
|
||||
<View> {
|
||||
width: Fit
|
||||
height: Fill
|
||||
show_bg: false
|
||||
// align:{ x: 0.5 }
|
||||
container = <RoundedView> {
|
||||
width: Fit
|
||||
height: Fill
|
||||
// flow: Down
|
||||
align: {x: 0.5, y: 0.0}
|
||||
// margin: {left: 200.0, right: 200.0, top: 100.0, bottom: 100.0}
|
||||
spacing: 2.0
|
||||
draw_bg: {
|
||||
color: #ddd,
|
||||
// border_width: 1.0,
|
||||
// border_color: #000,
|
||||
radius: (CARD_CORNER_RADIUS)
|
||||
}
|
||||
<View> {
|
||||
width: Fit, height: Fill
|
||||
flow: Down,
|
||||
align: {x: 0, y: 0.5}, padding: {top: 5., left: 10., right: 10.}, spacing: 20.
|
||||
username = <Label> {
|
||||
// padding: {top: 50., left: 140.}
|
||||
draw_text:{
|
||||
color: #000,
|
||||
text_style: <TEXT_SUB>{font_size: 20.},
|
||||
}
|
||||
text:"Welcome To Simple Nav"
|
||||
}
|
||||
// <FillerY> {}
|
||||
message_input = <SearchBar> {
|
||||
show_bg: false
|
||||
input = {
|
||||
width: Fill, height: Fit, margin: 0
|
||||
empty_message: "Enter Your Name"
|
||||
draw_bg: {
|
||||
color: #fff
|
||||
border_width: 0.5,
|
||||
border_color: #b4b4b4,
|
||||
}
|
||||
}
|
||||
}
|
||||
welcome_frame = <CustomButton> {
|
||||
button = {
|
||||
// width: Fill, height: Fit, margin: 0
|
||||
text: "Welcome"
|
||||
}
|
||||
}
|
||||
// <FillerY> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WelcomeScreen = <View> {
|
||||
width: Fill, height: Fill
|
||||
<Welcome> {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live)]
|
||||
pub struct Welcome {
|
||||
#[live]
|
||||
view:View,
|
||||
}
|
||||
|
||||
impl LiveHook for Welcome {
|
||||
fn before_live_design(cx: &mut Cx) {
|
||||
register_widget!(cx, Welcome);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Welcome {
|
||||
fn handle_widget_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem),
|
||||
) {
|
||||
let uid = self.widget_uid();
|
||||
self.handle_event_with(cx, event, &mut |cx, action: StackViewAction| {
|
||||
dispatch_action(cx, WidgetActionItem::new(action.into(), uid));
|
||||
});
|
||||
}
|
||||
|
||||
fn redraw(&mut self, cx: &mut Cx) {
|
||||
self.view.redraw(cx);
|
||||
}
|
||||
|
||||
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
|
||||
self.view.find_widgets(path, cached, results);
|
||||
}
|
||||
|
||||
fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
|
||||
self.view.draw_walk_widget(cx, walk)
|
||||
}
|
||||
}
|
||||
|
||||
impl Welcome {
|
||||
fn handle_event_with(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
event: &Event,
|
||||
dispatch_action: &mut dyn FnMut(&mut Cx, StackViewAction),
|
||||
) {
|
||||
let actions = self.view.handle_widget_event(cx, event);
|
||||
|
||||
if self
|
||||
.view
|
||||
.clickable_view(id!(welcome_frame))
|
||||
.clicked(&actions)
|
||||
{
|
||||
dispatch_action(cx, StackViewAction::ShowCounterScreen);
|
||||
}
|
||||
}
|
||||
}
|