Change memory regions to prevent crashes; Improve GUI
|
|
@ -95,6 +95,14 @@ mod console;
|
||||||
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
||||||
esp_bootloader_esp_idf::esp_app_desc!();
|
esp_bootloader_esp_idf::esp_app_desc!();
|
||||||
|
|
||||||
|
// Memory allocation regions.
|
||||||
|
// These can be debugged using `xtensa-esp32s3-elf-size -A <path-to-binary>`.
|
||||||
|
|
||||||
|
/// Total heap size
|
||||||
|
const HEAP_SIZE: usize = 128 * 1024;
|
||||||
|
/// Size of the app core's stack
|
||||||
|
const STACK_SIZE_CORE_APP: usize = 64 * 1024;
|
||||||
|
|
||||||
// const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS
|
// const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS
|
||||||
const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS
|
const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS
|
||||||
|
|
||||||
|
|
@ -149,12 +157,10 @@ async fn main(_spawner: Spawner) {
|
||||||
let alt_uart_rx_task = async {};
|
let alt_uart_rx_task = async {};
|
||||||
|
|
||||||
// Use the internal DRAM as the heap.
|
// Use the internal DRAM as the heap.
|
||||||
// TODO: Can we combine the regular link section with dram2?
|
// Memory reclaimed from the esp-idf bootloader.
|
||||||
// esp_alloc::heap_allocator!(size: 80 * 1024);
|
const HEAP_SIZE_RECLAIMED: usize = 64 * 1024;
|
||||||
// esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 72 * 1024);
|
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED);
|
||||||
//esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024);
|
esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED);
|
||||||
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 64 * 1024);
|
|
||||||
esp_alloc::heap_allocator!(size: 32 * 1024);
|
|
||||||
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
|
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
|
||||||
|
|
||||||
// Initialize the PSRAM allocator.
|
// Initialize the PSRAM allocator.
|
||||||
|
|
@ -235,15 +241,15 @@ async fn main(_spawner: Spawner) {
|
||||||
// Initialize USB
|
// Initialize USB
|
||||||
#[cfg(not(feature = "no-usb"))]
|
#[cfg(not(feature = "no-usb"))]
|
||||||
let usb_driver = {
|
let usb_driver = {
|
||||||
use core::ptr::addr_of_mut;
|
|
||||||
use esp_hal::otg_fs::Usb;
|
use esp_hal::otg_fs::Usb;
|
||||||
use esp_hal::otg_fs::asynch::{Config, Driver};
|
use esp_hal::otg_fs::asynch::{Config, Driver};
|
||||||
|
|
||||||
static mut EP_MEMORY: [u8; 1024] = [0; 1024];
|
static EP_MEMORY: StaticCell<[u8; 1024]> = StaticCell::new();
|
||||||
|
let ep_memory = EP_MEMORY.init_with(|| [0_u8; _]);
|
||||||
let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19);
|
let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19);
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
let driver = Driver::new(usb, unsafe { &mut *addr_of_mut!(EP_MEMORY) }, config);
|
let driver = Driver::new(usb, ep_memory, config);
|
||||||
|
|
||||||
info!("USB driver for RMK built!");
|
info!("USB driver for RMK built!");
|
||||||
|
|
||||||
|
|
@ -381,7 +387,7 @@ async fn main(_spawner: Spawner) {
|
||||||
let window_size = [framebuffer.height, framebuffer.width];
|
let window_size = [framebuffer.height, framebuffer.width];
|
||||||
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
|
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
|
||||||
|
|
||||||
static SECOND_CORE_STACK: StaticCell<Stack<{ 16 * 1024 }>> = StaticCell::new();
|
static SECOND_CORE_STACK: StaticCell<Stack<STACK_SIZE_CORE_APP>> = StaticCell::new();
|
||||||
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
||||||
esp_rtos::start_second_core(
|
esp_rtos::start_second_core(
|
||||||
peripherals.CPU_CTRL,
|
peripherals.CPU_CTRL,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use alloc::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
|
rc::Rc,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
|
|
@ -25,7 +26,7 @@ use itertools::Itertools;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rmk::futures::TryFutureExt;
|
use rmk::futures::TryFutureExt;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slint::SharedString;
|
use slint::{ModelRc, SharedString, StandardListViewItem, VecModel};
|
||||||
use spectre_api_sys::{
|
use spectre_api_sys::{
|
||||||
SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreResultType, SpectreUserKey,
|
SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreResultType, SpectreUserKey,
|
||||||
};
|
};
|
||||||
|
|
@ -243,7 +244,25 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
|
||||||
|
|
||||||
let main = AppWindow::new().unwrap();
|
let main = AppWindow::new().unwrap();
|
||||||
|
|
||||||
main.on_accepted(|string| {
|
main.on_login_pw_accepted({
|
||||||
|
let main = main.clone_strong();
|
||||||
|
move |username, password| {
|
||||||
|
info!("username = {username:?}, password = {password:?}");
|
||||||
|
main.set_app_state(AppState::UserSites);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// let sites = Rc::new(VecModel::default());
|
||||||
|
// sites.push("First".into());
|
||||||
|
// sites.push("Second".into());
|
||||||
|
// main.set_sites(ModelRc::new(ModelRc::new(sites.clone()).map(
|
||||||
|
// |mut site: StandardListViewItem| {
|
||||||
|
// site.text += "10";
|
||||||
|
// site
|
||||||
|
// },
|
||||||
|
// )));
|
||||||
|
|
||||||
|
main.on_site_pw_accepted(|string| {
|
||||||
warn!("Accepted: {string}");
|
warn!("Accepted: {string}");
|
||||||
let Ok(c_string) = CString::new(&*string) else {
|
let Ok(c_string) = CString::new(&*string) else {
|
||||||
warn!("String cannot be converted to a C string: {string:?}");
|
warn!("String cannot be converted to a C string: {string:?}");
|
||||||
|
|
|
||||||
3
firmware/acid-firmware/ui/globals.slint
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export global Style {
|
||||||
|
in property <length> spacing: 8px;
|
||||||
|
}
|
||||||
1
firmware/acid-firmware/ui/images/help-circle.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-help-circle"><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><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
||||||
|
After Width: | Height: | Size: 365 B |
1
firmware/acid-firmware/ui/images/key.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-key"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path></svg>
|
||||||
|
After Width: | Height: | Size: 352 B |
1
firmware/acid-firmware/ui/images/log-in.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-log-in"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line></svg>
|
||||||
|
After Width: | Height: | Size: 368 B |
1
firmware/acid-firmware/ui/images/log-out.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-log-out"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
|
||||||
|
After Width: | Height: | Size: 367 B |
1
firmware/acid-firmware/ui/images/sliders.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-sliders"><line x1="4" y1="21" x2="4" y2="14"></line><line x1="4" y1="10" x2="4" y2="3"></line><line x1="12" y1="21" x2="12" y2="12"></line><line x1="12" y1="8" x2="12" y2="3"></line><line x1="20" y1="21" x2="20" y2="16"></line><line x1="20" y1="12" x2="20" y2="3"></line><line x1="1" y1="14" x2="7" y2="14"></line><line x1="9" y1="8" x2="15" y2="8"></line><line x1="17" y1="16" x2="23" y2="16"></line></svg>
|
||||||
|
After Width: | Height: | Size: 611 B |
1
firmware/acid-firmware/ui/images/trash-2.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-trash-2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
||||||
|
After Width: | Height: | Size: 448 B |
1
firmware/acid-firmware/ui/images/users.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<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" class="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
|
||||||
|
After Width: | Height: | Size: 400 B |
38
firmware/acid-firmware/ui/login-view.slint
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { ComboBox, LineEdit } from "std-widgets.slint";
|
||||||
|
import { Style } from "globals.slint";
|
||||||
|
import { IconButton } from "widgets/icon-button.slint";
|
||||||
|
|
||||||
|
export component LoginView inherits VerticalLayout {
|
||||||
|
padding: Style.spacing;
|
||||||
|
spacing: Style.spacing;
|
||||||
|
callback pw_accepted(string, string);
|
||||||
|
Rectangle { }
|
||||||
|
|
||||||
|
HorizontalLayout {
|
||||||
|
spacing: Style.spacing;
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/users.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
combo_box_username := ComboBox {
|
||||||
|
model: ["first", "second"];
|
||||||
|
}
|
||||||
|
|
||||||
|
line_edit_user_pw := LineEdit {
|
||||||
|
input-type: InputType.password;
|
||||||
|
placeholder-text: "Password";
|
||||||
|
accepted(text) => {
|
||||||
|
root.pw_accepted(combo_box_username.current-value, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/log-in.svg");
|
||||||
|
clicked => {
|
||||||
|
root.pw_accepted(combo_box_username.current-value, line_edit_user_pw.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { }
|
||||||
|
}
|
||||||
|
|
@ -8,27 +8,46 @@ import {
|
||||||
Button,
|
Button,
|
||||||
} from "std-widgets.slint";
|
} from "std-widgets.slint";
|
||||||
*/
|
*/
|
||||||
import { Button, VerticalBox, LineEdit, GridBox } from "std-widgets.slint";
|
import {
|
||||||
|
Button,
|
||||||
|
VerticalBox,
|
||||||
|
LineEdit,
|
||||||
|
GridBox,
|
||||||
|
StandardListView,
|
||||||
|
ListView,
|
||||||
|
ComboBox,
|
||||||
|
} from "std-widgets.slint";
|
||||||
|
|
||||||
// See https://github.com/slint-ui/slint/issues/4956 for issues with fonts.
|
// See https://github.com/slint-ui/slint/issues/4956 for issues with fonts.
|
||||||
import "../fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf";
|
import "../fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf";
|
||||||
|
import { UserSitesView } from "user-sites-view.slint";
|
||||||
|
import { Style } from "globals.slint";
|
||||||
|
import { UsersView } from "users-view.slint";
|
||||||
|
import { LoginView } from "login-view.slint";
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: A bigger stack for the 2nd core might be needed to prevent crashing.
|
|
||||||
export enum AppState {
|
export enum AppState {
|
||||||
PasswordForm,
|
login,
|
||||||
|
user-sites,
|
||||||
|
users,
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
export component AppWindow inherits Window {
|
export component AppWindow inherits Window {
|
||||||
in property <string> dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°";
|
in property <string> dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°´ˇ¨";
|
||||||
default-font-family: "IBM Plex Mono";
|
default-font-family: "IBM Plex Mono";
|
||||||
default-font-size: 16pt;
|
default-font-size: 16pt;
|
||||||
height: 368px;
|
height: 368px;
|
||||||
width: 960px;
|
width: 960px;
|
||||||
in-out property <int> counter: 42;
|
in property <AppState> app-state: AppState.login;
|
||||||
callback request-increase-value();
|
// Login View
|
||||||
callback accepted(string);
|
callback login_pw_accepted <=> login_view.pw_accepted;
|
||||||
|
// Sites View
|
||||||
|
in property <[StandardListViewItem]> sites <=> user_sites_view.model;
|
||||||
|
callback site_pw_edited <=> user_sites_view.pw_edited;
|
||||||
|
callback site_pw_accepted <=> user_sites_view.pw_accepted;
|
||||||
|
// Sites View
|
||||||
|
in property <[StandardListViewItem]> usernames <=> users_view.model;
|
||||||
|
callback user_username_edited <=> users_view.pw_edited;
|
||||||
|
callback user_username_accepted <=> users_view.pw_accepted;
|
||||||
VerticalBox {
|
VerticalBox {
|
||||||
width: 960px;
|
width: 960px;
|
||||||
height: 368px;
|
height: 368px;
|
||||||
|
|
@ -36,53 +55,21 @@ export component AppWindow inherits Window {
|
||||||
padding-top: 120px;
|
padding-top: 120px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
height: 240px;
|
||||||
|
background: #00141d;
|
||||||
// For debugging bounds.
|
// For debugging bounds.
|
||||||
// background: #2c82ff;
|
|
||||||
// border-color: #ffcf00;
|
// border-color: #ffcf00;
|
||||||
// border-width: 1px;
|
// border-width: 1px;
|
||||||
GridBox {
|
login_view := LoginView {
|
||||||
VerticalBox {
|
visible: app-state == AppState.login;
|
||||||
Text {
|
}
|
||||||
text: "Counter: \{root.counter}";
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
user_sites_view := UserSitesView {
|
||||||
text: "Increase value";
|
visible: app-state == AppState.user-sites;
|
||||||
clicked => {
|
}
|
||||||
root.counter += 1;
|
|
||||||
root.request-increase-value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LineEdit {
|
users_view := UsersView {
|
||||||
input-type: InputType.text;
|
visible: app-state == AppState.users;
|
||||||
text: "LineEdit";
|
|
||||||
accepted(text) => {
|
|
||||||
root.accepted(text);
|
|
||||||
}
|
|
||||||
/*changed text => {
|
|
||||||
root.changed(self.text);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
TabWidget {
|
|
||||||
Tab {
|
|
||||||
title: "first";
|
|
||||||
Button {
|
|
||||||
text: "First";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tab {
|
|
||||||
title: "second";
|
|
||||||
Button {
|
|
||||||
text: "Second";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
firmware/acid-firmware/ui/user-sites-view.slint
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { LineEdit, StandardListView, Button } from "std-widgets.slint";
|
||||||
|
import { Style } from "globals.slint";
|
||||||
|
import { IconButton } from "widgets/icon-button.slint";
|
||||||
|
|
||||||
|
export component UserSitesView inherits HorizontalLayout {
|
||||||
|
padding: Style.spacing;
|
||||||
|
spacing: Style.spacing;
|
||||||
|
in property <[StandardListViewItem]> model <=> list_view_sites.model;
|
||||||
|
in-out property <int> current-item <=> list_view_sites.current-item;
|
||||||
|
callback pw_edited <=> line_edit_site_pw.edited;
|
||||||
|
callback pw_accepted <=> line_edit_site_pw.accepted;
|
||||||
|
VerticalLayout {
|
||||||
|
spacing: Style.spacing;
|
||||||
|
Text {
|
||||||
|
text: "Send password for:";
|
||||||
|
}
|
||||||
|
|
||||||
|
line_edit_site_pw := LineEdit {
|
||||||
|
input-type: InputType.text;
|
||||||
|
placeholder-text: "example.org";
|
||||||
|
}
|
||||||
|
|
||||||
|
list_view_sites := StandardListView {
|
||||||
|
model: [
|
||||||
|
{ text: "Test" },
|
||||||
|
{ text: "Test" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalLayout {
|
||||||
|
spacing: Style.spacing;
|
||||||
|
// IconButton {
|
||||||
|
// icon: @image-url("images/log-out.svg");
|
||||||
|
// }
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/sliders.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/help-circle.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/key.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/trash-2.svg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
firmware/acid-firmware/ui/users-view.slint
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { LineEdit, StandardListView, Button } from "std-widgets.slint";
|
||||||
|
import { Style } from "globals.slint";
|
||||||
|
import { IconButton } from "widgets/icon-button.slint";
|
||||||
|
|
||||||
|
export component UsersView inherits HorizontalLayout {
|
||||||
|
padding: Style.spacing;
|
||||||
|
spacing: Style.spacing;
|
||||||
|
in property <[StandardListViewItem]> model <=> list_view_sites.model;
|
||||||
|
in-out property <int> current-item <=> list_view_sites.current-item;
|
||||||
|
callback pw_edited <=> line_edit_site_pw.edited;
|
||||||
|
callback pw_accepted <=> line_edit_site_pw.accepted;
|
||||||
|
VerticalLayout {
|
||||||
|
spacing: Style.spacing;
|
||||||
|
Text {
|
||||||
|
text: "Username:";
|
||||||
|
}
|
||||||
|
|
||||||
|
line_edit_site_pw := LineEdit {
|
||||||
|
input-type: InputType.text;
|
||||||
|
placeholder-text: "Full Name";
|
||||||
|
}
|
||||||
|
|
||||||
|
list_view_sites := StandardListView {
|
||||||
|
model: [
|
||||||
|
{ text: "Test" },
|
||||||
|
{ text: "Test" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalLayout {
|
||||||
|
spacing: Style.spacing;
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/key.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: @image-url("images/trash-2.svg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
firmware/acid-firmware/ui/widgets/icon-button.slint
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Button } from "std-widgets.slint";
|
||||||
|
|
||||||
|
export component IconButton inherits Button {
|
||||||
|
colorize-icon: true;
|
||||||
|
icon-size: 24px;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||