0.1.20
This commit is contained in:
Generated
+23
-66
@@ -108,7 +108,7 @@ dependencies = [
|
||||
"hashbrown 0.16.1",
|
||||
"static_assertions",
|
||||
"windows",
|
||||
"windows-core 0.62.2",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1409,7 +1409,7 @@ dependencies = [
|
||||
"roxmltree",
|
||||
"smallvec",
|
||||
"windows",
|
||||
"windows-core 0.62.2",
|
||||
"windows-core",
|
||||
"yeslogic-fontconfig-sys",
|
||||
]
|
||||
|
||||
@@ -2109,7 +2109,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.58.0",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3904,10 +3904,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rcalc"
|
||||
version = "0.1.11"
|
||||
version = "0.1.20"
|
||||
dependencies = [
|
||||
"slint",
|
||||
"slint-build",
|
||||
"winresource",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5430,7 +5431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core 0.62.2",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-numerics",
|
||||
]
|
||||
@@ -5441,20 +5442,7 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||
dependencies = [
|
||||
"windows-implement 0.58.0",
|
||||
"windows-interface 0.58.0",
|
||||
"windows-result 0.2.0",
|
||||
"windows-strings 0.1.0",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5463,11 +5451,11 @@ version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement 0.60.2",
|
||||
"windows-interface 0.59.3",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings 0.5.1",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5476,22 +5464,11 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
@@ -5503,17 +5480,6 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
@@ -5537,19 +5503,10 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
@@ -5559,16 +5516,6 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result 0.2.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
@@ -5805,6 +5752,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
|
||||
dependencies = [
|
||||
"toml",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rcalc"
|
||||
version = "0.1.12"
|
||||
version = "0.1.21"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
@@ -9,3 +9,4 @@ slint = "1.11"
|
||||
|
||||
[build-dependencies]
|
||||
slint-build = "1.11"
|
||||
winresource = "0.1"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 279 KiB |
@@ -7,9 +7,35 @@ fn main() {
|
||||
let version = resolve_app_version();
|
||||
println!("cargo:rustc-env=APP_VERSION={version}");
|
||||
|
||||
embed_windows_icon();
|
||||
|
||||
slint_build::compile("ui/app.slint").expect("Slint build failed");
|
||||
}
|
||||
|
||||
/// Shell icon for `rcalc.exe` in Explorer (needs `assets/icon.ico` + windres/rc).
|
||||
fn embed_windows_icon() {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
||||
if target_os != "windows" {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=assets/icon.ico");
|
||||
|
||||
let mut res = winresource::WindowsResource::new();
|
||||
res.set_icon("assets/icon.ico");
|
||||
|
||||
// Cross-compile from Linux: mingw windres is prefixed.
|
||||
if cfg!(not(windows)) {
|
||||
let target = env::var("TARGET").unwrap_or_default();
|
||||
if target.contains("windows-gnu") {
|
||||
res.set_windres_path("x86_64-w64-mingw32-windres");
|
||||
}
|
||||
}
|
||||
|
||||
res.compile()
|
||||
.expect("failed to embed Windows icon (is windres/rc available?)");
|
||||
}
|
||||
|
||||
/// Window / package version shown as `Rcalc-{version}`.
|
||||
///
|
||||
/// On each `release` build the patch in `Cargo.toml` is incremented once
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+248
@@ -0,0 +1,248 @@
|
||||
//! Persistent app settings (`~/.config/rcalc/config.json` or `%APPDATA%\rcalc\config.json`).
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ThemeId {
|
||||
Dark = 0,
|
||||
Light = 1,
|
||||
Blue = 2,
|
||||
}
|
||||
|
||||
impl ThemeId {
|
||||
pub fn from_i32(v: i32) -> Self {
|
||||
match v {
|
||||
1 => Self::Light,
|
||||
2 => Self::Blue,
|
||||
_ => Self::Dark,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_i32(self) -> i32 {
|
||||
self as i32
|
||||
}
|
||||
|
||||
pub fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::Dark => "Dark",
|
||||
Self::Light => "Light",
|
||||
Self::Blue => "Blue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppConfig {
|
||||
/// Button size: 1 = compact, 2 = normal, 3 = large.
|
||||
pub button_size: u8,
|
||||
pub theme: ThemeId,
|
||||
/// Last calculator mode index (0..4).
|
||||
pub last_mode: u8,
|
||||
pub show_bits: bool,
|
||||
/// Programmer word size label: QWORD, DWORD, DINT, WORD, INT, BYTE.
|
||||
pub word_size: String,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
button_size: 2,
|
||||
theme: ThemeId::Dark,
|
||||
last_mode: 0,
|
||||
show_bits: true,
|
||||
word_size: "QWORD".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
pub fn load() -> Self {
|
||||
let path = config_path();
|
||||
let Ok(raw) = fs::read_to_string(&path) else {
|
||||
return Self::default();
|
||||
};
|
||||
Self::from_json(&raw).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
let path = config_path();
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
let _ = fs::write(path, self.to_json());
|
||||
}
|
||||
|
||||
pub fn clamp_button_size(size: u8) -> u8 {
|
||||
size.clamp(1, 3)
|
||||
}
|
||||
|
||||
fn to_json(&self) -> String {
|
||||
format!(
|
||||
"{{\n \"button_size\": {},\n \"theme\": {},\n \"last_mode\": {},\n \"show_bits\": {},\n \"word_size\": \"{}\"\n}}\n",
|
||||
self.button_size,
|
||||
self.theme.as_i32(),
|
||||
self.last_mode,
|
||||
self.show_bits,
|
||||
self.word_size
|
||||
)
|
||||
}
|
||||
|
||||
fn from_json(raw: &str) -> Option<Self> {
|
||||
let mut cfg = Self::default();
|
||||
for line in raw.lines() {
|
||||
let line = line.trim().trim_end_matches(',');
|
||||
if let Some(v) = json_u8(line, "button_size") {
|
||||
cfg.button_size = Self::clamp_button_size(v);
|
||||
} else if let Some(v) = json_i32(line, "theme") {
|
||||
cfg.theme = ThemeId::from_i32(v);
|
||||
} else if let Some(v) = json_u8(line, "last_mode") {
|
||||
cfg.last_mode = v.min(4);
|
||||
} else if let Some(v) = json_bool(line, "show_bits") {
|
||||
cfg.show_bits = v;
|
||||
} else if let Some(v) = json_string(line, "word_size") {
|
||||
let upper = v.to_ascii_uppercase();
|
||||
if matches!(
|
||||
upper.as_str(),
|
||||
"QWORD" | "DWORD" | "DINT" | "WORD" | "INT" | "BYTE"
|
||||
) {
|
||||
cfg.word_size = upper;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(cfg)
|
||||
}
|
||||
}
|
||||
|
||||
fn json_u8(line: &str, key: &str) -> Option<u8> {
|
||||
json_i32(line, key)?.try_into().ok()
|
||||
}
|
||||
|
||||
fn json_i32(line: &str, key: &str) -> Option<i32> {
|
||||
let prefix = format!("\"{key}\"");
|
||||
let idx = line.find(&prefix)?;
|
||||
let rest = &line[idx + prefix.len()..];
|
||||
let rest = rest.trim_start().trim_start_matches(':').trim_start();
|
||||
rest.split(|c: char| c == ',' || c == '}' || c.is_whitespace())
|
||||
.next()?
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
|
||||
fn json_bool(line: &str, key: &str) -> Option<bool> {
|
||||
let prefix = format!("\"{key}\"");
|
||||
let idx = line.find(&prefix)?;
|
||||
let rest = &line[idx + prefix.len()..];
|
||||
let rest = rest.trim_start().trim_start_matches(':').trim_start();
|
||||
if rest.starts_with("true") {
|
||||
Some(true)
|
||||
} else if rest.starts_with("false") {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn json_string(line: &str, key: &str) -> Option<String> {
|
||||
let prefix = format!("\"{key}\"");
|
||||
let idx = line.find(&prefix)?;
|
||||
let rest = &line[idx + prefix.len()..];
|
||||
let rest = rest.trim_start().trim_start_matches(':').trim_start();
|
||||
let rest = rest.strip_prefix('"')?;
|
||||
let end = rest.find('"')?;
|
||||
Some(rest[..end].to_string())
|
||||
}
|
||||
|
||||
pub fn config_path() -> PathBuf {
|
||||
config_dir().join("config.json")
|
||||
}
|
||||
|
||||
fn config_dir() -> PathBuf {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Some(appdata) = std::env::var_os("APPDATA") {
|
||||
return PathBuf::from(appdata).join("rcalc");
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if let Some(xdg) = std::env::var_os("XDG_CONFIG_HOME") {
|
||||
return PathBuf::from(xdg).join("rcalc");
|
||||
}
|
||||
if let Some(home) = std::env::var_os("HOME") {
|
||||
return PathBuf::from(home).join(".config").join("rcalc");
|
||||
}
|
||||
}
|
||||
PathBuf::from(".")
|
||||
}
|
||||
|
||||
/// RGB colors for a theme (applied to Slint global).
|
||||
pub struct ThemeColors {
|
||||
pub bg: (u8, u8, u8),
|
||||
pub surface: (u8, u8, u8),
|
||||
pub btn: (u8, u8, u8),
|
||||
pub btn_hover: (u8, u8, u8),
|
||||
pub digit: (u8, u8, u8),
|
||||
pub digit_hover: (u8, u8, u8),
|
||||
pub accent: (u8, u8, u8),
|
||||
pub accent_hover: (u8, u8, u8),
|
||||
pub text: (u8, u8, u8),
|
||||
pub text_muted: (u8, u8, u8),
|
||||
pub text_dim: (u8, u8, u8),
|
||||
pub menu_active: (u8, u8, u8),
|
||||
pub eq_text: (u8, u8, u8),
|
||||
pub border: (u8, u8, u8),
|
||||
}
|
||||
|
||||
pub fn theme_colors(id: ThemeId) -> ThemeColors {
|
||||
match id {
|
||||
ThemeId::Dark => ThemeColors {
|
||||
bg: (0x20, 0x20, 0x20),
|
||||
surface: (0x2a, 0x2a, 0x2a),
|
||||
btn: (0x32, 0x32, 0x32),
|
||||
btn_hover: (0x46, 0x46, 0x46),
|
||||
digit: (0x3b, 0x3b, 0x3b),
|
||||
digit_hover: (0x50, 0x50, 0x50),
|
||||
accent: (0x60, 0xcd, 0xff),
|
||||
accent_hover: (0x78, 0xd7, 0xff),
|
||||
text: (0xff, 0xff, 0xff),
|
||||
text_muted: (0xc8, 0xc8, 0xc8),
|
||||
text_dim: (0x9a, 0x9a, 0x9a),
|
||||
menu_active: (0x2f, 0x3a, 0x42),
|
||||
eq_text: (0x14, 0x14, 0x14),
|
||||
border: (0x40, 0x40, 0x40),
|
||||
},
|
||||
ThemeId::Light => ThemeColors {
|
||||
bg: (0xf3, 0xf3, 0xf3),
|
||||
surface: (0xff, 0xff, 0xff),
|
||||
btn: (0xe6, 0xe6, 0xe6),
|
||||
btn_hover: (0xd0, 0xd0, 0xd0),
|
||||
digit: (0xff, 0xff, 0xff),
|
||||
digit_hover: (0xee, 0xee, 0xee),
|
||||
accent: (0x00, 0x78, 0xd4),
|
||||
accent_hover: (0x1a, 0x86, 0xda),
|
||||
text: (0x1a, 0x1a, 0x1a),
|
||||
text_muted: (0x55, 0x55, 0x55),
|
||||
text_dim: (0x77, 0x77, 0x77),
|
||||
menu_active: (0xd6, 0xeb, 0xf8),
|
||||
eq_text: (0xff, 0xff, 0xff),
|
||||
border: (0xcc, 0xcc, 0xcc),
|
||||
},
|
||||
ThemeId::Blue => ThemeColors {
|
||||
bg: (0x0f, 0x1c, 0x2e),
|
||||
surface: (0x16, 0x28, 0x40),
|
||||
btn: (0x1e, 0x3a, 0x5f),
|
||||
btn_hover: (0x2a, 0x4d, 0x7a),
|
||||
digit: (0x24, 0x45, 0x6e),
|
||||
digit_hover: (0x2f, 0x58, 0x88),
|
||||
accent: (0x4f, 0xc3, 0xf7),
|
||||
accent_hover: (0x81, 0xd4, 0xfa),
|
||||
text: (0xec, 0xf5, 0xff),
|
||||
text_muted: (0xb0, 0xc4, 0xde),
|
||||
text_dim: (0x7f, 0x9b, 0xb8),
|
||||
menu_active: (0x1a, 0x3a, 0x5c),
|
||||
eq_text: (0x0a, 0x14, 0x22),
|
||||
border: (0x2a, 0x4a, 0x6e),
|
||||
},
|
||||
}
|
||||
}
|
||||
+168
-39
@@ -1,6 +1,7 @@
|
||||
// Hide the extra console window on Windows GUI builds.
|
||||
#![cfg_attr(all(windows, not(debug_assertions)), windows_subsystem = "windows")]
|
||||
|
||||
mod config;
|
||||
mod convert;
|
||||
mod crc;
|
||||
mod engine;
|
||||
@@ -10,11 +11,12 @@ mod programmer;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use config::{theme_colors, AppConfig, ThemeId};
|
||||
use convert::{ConvField, ConvPanel, Converter, Endian, RatioWidth};
|
||||
use crc::CrcTool;
|
||||
use engine::{Calculator, Op, StdPanel};
|
||||
use engineering::Engineering;
|
||||
use programmer::{Base, ProgOp, Programmer};
|
||||
use programmer::{Base, ProgOp, Programmer, WordSize};
|
||||
use slint::{ComponentHandle, ModelRc, VecModel};
|
||||
|
||||
slint::include_modules!();
|
||||
@@ -26,6 +28,42 @@ enum Mode {
|
||||
Programmer,
|
||||
Convert,
|
||||
Crc,
|
||||
Settings,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
fn from_u8(v: u8) -> Self {
|
||||
match v {
|
||||
1 => Self::Engineering,
|
||||
2 => Self::Programmer,
|
||||
3 => Self::Convert,
|
||||
4 => Self::Crc,
|
||||
5 => Self::Settings,
|
||||
_ => Self::Standard,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_u8(self) -> u8 {
|
||||
match self {
|
||||
Self::Standard => 0,
|
||||
Self::Engineering => 1,
|
||||
Self::Programmer => 2,
|
||||
Self::Convert => 3,
|
||||
Self::Crc => 4,
|
||||
Self::Settings => 5,
|
||||
}
|
||||
}
|
||||
|
||||
fn title(self) -> &'static str {
|
||||
match self {
|
||||
Self::Standard => "Standard",
|
||||
Self::Engineering => "Engineering",
|
||||
Self::Programmer => "Programmer",
|
||||
Self::Convert => "Convert",
|
||||
Self::Crc => "CRC",
|
||||
Self::Settings => "Settings",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
@@ -37,19 +75,26 @@ struct State {
|
||||
crc: CrcTool,
|
||||
show_bits: bool,
|
||||
win_profile: u32,
|
||||
config: AppConfig,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
let config = AppConfig::load();
|
||||
let mut prog = Programmer::new();
|
||||
if let Some(ws) = WordSize::from_label(&config.word_size) {
|
||||
prog.set_word_size(ws);
|
||||
}
|
||||
Self {
|
||||
mode: Mode::Standard,
|
||||
mode: Mode::from_u8(config.last_mode),
|
||||
std: Calculator::new(),
|
||||
eng: Engineering::new(),
|
||||
prog: Programmer::new(),
|
||||
prog,
|
||||
conv: Converter::new(),
|
||||
crc: CrcTool::new(),
|
||||
show_bits: true,
|
||||
show_bits: config.show_bits,
|
||||
win_profile: u32::MAX,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,8 +130,11 @@ fn run_app() -> Result<(), slint::PlatformError> {
|
||||
ui.set_app_title(format!("Rcalc-{}", env!("APP_VERSION")).into());
|
||||
|
||||
let state = Rc::new(RefCell::new(State::default()));
|
||||
|
||||
refresh(&ui, &mut state.borrow_mut());
|
||||
{
|
||||
let mut s = state.borrow_mut();
|
||||
apply_ui_style(&ui, &s.config);
|
||||
refresh(&ui, &mut s);
|
||||
}
|
||||
|
||||
let ui_weak = ui.as_weak();
|
||||
let state_keys = state.clone();
|
||||
@@ -94,6 +142,7 @@ fn run_app() -> Result<(), slint::PlatformError> {
|
||||
let mut s = state_keys.borrow_mut();
|
||||
handle_key(&mut s, id.as_str());
|
||||
if let Some(ui) = ui_weak.upgrade() {
|
||||
apply_ui_style(&ui, &s.config);
|
||||
refresh(&ui, &mut s);
|
||||
}
|
||||
});
|
||||
@@ -111,6 +160,44 @@ fn run_app() -> Result<(), slint::PlatformError> {
|
||||
ui.run()
|
||||
}
|
||||
|
||||
fn rgb(c: (u8, u8, u8)) -> slint::Color {
|
||||
slint::Color::from_rgb_u8(c.0, c.1, c.2)
|
||||
}
|
||||
|
||||
fn apply_ui_style(ui: &AppWindow, cfg: &AppConfig) {
|
||||
let style = ui.global::<UiStyle>();
|
||||
style.set_size(i32::from(cfg.button_size));
|
||||
style.set_theme(cfg.theme.as_i32());
|
||||
let c = theme_colors(cfg.theme);
|
||||
style.set_bg(rgb(c.bg));
|
||||
style.set_surface(rgb(c.surface));
|
||||
style.set_btn(rgb(c.btn));
|
||||
style.set_btn_hover(rgb(c.btn_hover));
|
||||
style.set_digit(rgb(c.digit));
|
||||
style.set_digit_hover(rgb(c.digit_hover));
|
||||
style.set_accent(rgb(c.accent));
|
||||
style.set_accent_hover(rgb(c.accent_hover));
|
||||
style.set_text(rgb(c.text));
|
||||
style.set_text_muted(rgb(c.text_muted));
|
||||
style.set_text_dim(rgb(c.text_dim));
|
||||
style.set_menu_active(rgb(c.menu_active));
|
||||
style.set_eq_text(rgb(c.eq_text));
|
||||
style.set_border(rgb(c.border));
|
||||
|
||||
ui.set_cfg_button_size(i32::from(cfg.button_size));
|
||||
ui.set_cfg_theme(cfg.theme.as_i32());
|
||||
}
|
||||
|
||||
fn persist_config(state: &mut State) {
|
||||
if state.mode != Mode::Settings {
|
||||
state.config.last_mode = state.mode.as_u8();
|
||||
}
|
||||
state.config.show_bits = state.show_bits;
|
||||
state.config.word_size = state.prog.word_size().label().to_string();
|
||||
state.config.button_size = AppConfig::clamp_button_size(state.config.button_size);
|
||||
state.config.save();
|
||||
}
|
||||
|
||||
fn install_panic_hook() {
|
||||
let default = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
@@ -173,13 +260,7 @@ fn bit_row_model(row: &[(bool, u32)]) -> ModelRc<BitInfo> {
|
||||
}
|
||||
|
||||
fn window_profile(state: &State) -> u32 {
|
||||
let mode = match state.mode {
|
||||
Mode::Standard => 0,
|
||||
Mode::Engineering => 1,
|
||||
Mode::Programmer => 2,
|
||||
Mode::Convert => 3,
|
||||
Mode::Crc => 4,
|
||||
};
|
||||
let mode = u32::from(state.mode.as_u8());
|
||||
let bits = if state.show_bits {
|
||||
state.prog.word_size().bits()
|
||||
} else {
|
||||
@@ -190,7 +271,9 @@ fn window_profile(state: &State) -> u32 {
|
||||
ConvPanel::Ratio => 1,
|
||||
ConvPanel::Ascii => 2,
|
||||
};
|
||||
mode * 1000 + bits * 10 + panel
|
||||
let size = u32::from(state.config.button_size);
|
||||
let theme = state.config.theme.as_i32() as u32;
|
||||
mode * 100_000 + bits * 1000 + panel * 100 + size * 10 + theme
|
||||
}
|
||||
|
||||
fn fit_window(ui: &AppWindow, state: &mut State) {
|
||||
@@ -200,6 +283,12 @@ fn fit_window(ui: &AppWindow, state: &mut State) {
|
||||
}
|
||||
state.win_profile = profile;
|
||||
|
||||
let scale = match state.config.button_size {
|
||||
1 => 0.92,
|
||||
3 => 1.12,
|
||||
_ => 1.0,
|
||||
};
|
||||
|
||||
let (w, h) = match state.mode {
|
||||
Mode::Standard => (360.0, 580.0),
|
||||
Mode::Engineering => (420.0, 720.0),
|
||||
@@ -218,9 +307,10 @@ fn fit_window(ui: &AppWindow, state: &mut State) {
|
||||
}
|
||||
Mode::Convert => (420.0, 820.0),
|
||||
Mode::Crc => (460.0, 560.0),
|
||||
Mode::Settings => (420.0, 420.0),
|
||||
};
|
||||
ui.window()
|
||||
.set_size(slint::LogicalSize::new(w as f32, h as f32));
|
||||
.set_size(slint::LogicalSize::new((w * scale) as f32, (h * scale) as f32));
|
||||
}
|
||||
|
||||
fn apply_crc_results(ui: &AppWindow, state: &State) {
|
||||
@@ -236,24 +326,11 @@ fn apply_crc_results(ui: &AppWindow, state: &State) {
|
||||
}
|
||||
|
||||
fn refresh(ui: &AppWindow, state: &mut State) {
|
||||
ui.set_mode(match state.mode {
|
||||
Mode::Standard => 0,
|
||||
Mode::Engineering => 1,
|
||||
Mode::Programmer => 2,
|
||||
Mode::Convert => 3,
|
||||
Mode::Crc => 4,
|
||||
});
|
||||
ui.set_mode_title(
|
||||
match state.mode {
|
||||
Mode::Standard => "Standard",
|
||||
Mode::Engineering => "Engineering",
|
||||
Mode::Programmer => "Programmer",
|
||||
Mode::Convert => "Convert",
|
||||
Mode::Crc => "CRC",
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
ui.set_mode(i32::from(state.mode.as_u8()));
|
||||
ui.set_mode_title(state.mode.title().into());
|
||||
ui.set_menu_open(false);
|
||||
ui.set_cfg_button_size(i32::from(state.config.button_size));
|
||||
ui.set_cfg_theme(state.config.theme.as_i32());
|
||||
|
||||
fit_window(ui, state);
|
||||
|
||||
@@ -335,29 +412,75 @@ fn refresh(ui: &AppWindow, state: &mut State) {
|
||||
ui.set_crc_input(state.crc.input().into());
|
||||
apply_crc_results(ui, state);
|
||||
}
|
||||
Mode::Settings => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mode(state: &mut State, mode: Mode) {
|
||||
state.mode = mode;
|
||||
persist_config(state);
|
||||
}
|
||||
|
||||
fn handle_key(state: &mut State, id: &str) {
|
||||
match id {
|
||||
"mode:std" => {
|
||||
state.mode = Mode::Standard;
|
||||
set_mode(state, Mode::Standard);
|
||||
return;
|
||||
}
|
||||
"mode:eng" => {
|
||||
state.mode = Mode::Engineering;
|
||||
set_mode(state, Mode::Engineering);
|
||||
return;
|
||||
}
|
||||
"mode:prog" => {
|
||||
state.mode = Mode::Programmer;
|
||||
set_mode(state, Mode::Programmer);
|
||||
return;
|
||||
}
|
||||
"mode:conv" => {
|
||||
state.mode = Mode::Convert;
|
||||
set_mode(state, Mode::Convert);
|
||||
return;
|
||||
}
|
||||
"mode:crc" => {
|
||||
state.mode = Mode::Crc;
|
||||
set_mode(state, Mode::Crc);
|
||||
return;
|
||||
}
|
||||
"mode:settings" => {
|
||||
state.mode = Mode::Settings;
|
||||
return;
|
||||
}
|
||||
"cfg:size:1" => {
|
||||
state.config.button_size = 1;
|
||||
state.win_profile = u32::MAX;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
"cfg:size:2" => {
|
||||
state.config.button_size = 2;
|
||||
state.win_profile = u32::MAX;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
"cfg:size:3" => {
|
||||
state.config.button_size = 3;
|
||||
state.win_profile = u32::MAX;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
"cfg:theme:0" => {
|
||||
state.config.theme = ThemeId::Dark;
|
||||
state.win_profile = u32::MAX;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
"cfg:theme:1" => {
|
||||
state.config.theme = ThemeId::Light;
|
||||
state.win_profile = u32::MAX;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
"cfg:theme:2" => {
|
||||
state.config.theme = ThemeId::Blue;
|
||||
state.win_profile = u32::MAX;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
@@ -368,7 +491,7 @@ fn handle_key(state: &mut State, id: &str) {
|
||||
Mode::Engineering => handle_engineering(&mut state.eng, id),
|
||||
Mode::Programmer => handle_programmer(state, id),
|
||||
Mode::Convert => handle_convert(&mut state.conv, id),
|
||||
Mode::Crc => {}
|
||||
Mode::Crc | Mode::Settings => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,6 +579,13 @@ fn handle_standard(calc: &mut Calculator, id: &str) {
|
||||
fn handle_programmer(state: &mut State, id: &str) {
|
||||
if id == "bits" {
|
||||
state.show_bits = !state.show_bits;
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
|
||||
if id == "word" {
|
||||
state.prog.cycle_word_size();
|
||||
persist_config(state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -476,7 +606,6 @@ fn handle_programmer(state: &mut State, id: &str) {
|
||||
"base:dec" => calc.set_base(Base::Dec),
|
||||
"base:oct" => calc.set_base(Base::Oct),
|
||||
"base:bin" => calc.set_base(Base::Bin),
|
||||
"word" => calc.cycle_word_size(),
|
||||
"+" => calc.set_op(ProgOp::Add),
|
||||
"-" | "−" => calc.set_op(ProgOp::Sub),
|
||||
"*" | "×" => calc.set_op(ProgOp::Mul),
|
||||
|
||||
+23
-1
@@ -75,6 +75,18 @@ impl WordSize {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_label(s: &str) -> Option<Self> {
|
||||
match s.trim().to_ascii_uppercase().as_str() {
|
||||
"QWORD" => Some(WordSize::Qword),
|
||||
"DWORD" => Some(WordSize::Dword),
|
||||
"DINT" => Some(WordSize::Dint),
|
||||
"WORD" => Some(WordSize::Word),
|
||||
"INT" => Some(WordSize::Int),
|
||||
"BYTE" => Some(WordSize::Byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle(self) -> Self {
|
||||
match self {
|
||||
WordSize::Qword => WordSize::Dword,
|
||||
@@ -310,7 +322,17 @@ impl Programmer {
|
||||
}
|
||||
self.typing = false;
|
||||
}
|
||||
self.word_size = self.word_size.cycle();
|
||||
self.set_word_size(self.word_size.cycle());
|
||||
}
|
||||
|
||||
pub fn set_word_size(&mut self, size: WordSize) {
|
||||
if self.typing {
|
||||
if let Ok(v) = parse_entry(&self.entry, self.base) {
|
||||
self.value = v;
|
||||
}
|
||||
self.typing = false;
|
||||
}
|
||||
self.word_size = size;
|
||||
self.value &= self.word_size.mask();
|
||||
if let Some(p) = self.pending.as_mut() {
|
||||
*p &= self.word_size.mask();
|
||||
|
||||
+226
-112
@@ -3,17 +3,46 @@ export struct BitInfo {
|
||||
index: int,
|
||||
}
|
||||
|
||||
export global UiStyle {
|
||||
in-out property <int> size: 2;
|
||||
in-out property <int> theme: 0;
|
||||
|
||||
in-out property <color> bg: #202020;
|
||||
in-out property <color> surface: #2a2a2a;
|
||||
in-out property <color> btn: #323232;
|
||||
in-out property <color> btn-hover: #464646;
|
||||
in-out property <color> digit: #3b3b3b;
|
||||
in-out property <color> digit-hover: #505050;
|
||||
in-out property <color> accent: #60cdff;
|
||||
in-out property <color> accent-hover: #78d7ff;
|
||||
in-out property <color> text: #ffffff;
|
||||
in-out property <color> text-muted: #c8c8c8;
|
||||
in-out property <color> text-dim: #9a9a9a;
|
||||
in-out property <color> menu-active: #2f3a42;
|
||||
in-out property <color> eq-text: #141414;
|
||||
in-out property <color> border: #404040;
|
||||
|
||||
in-out property <length> btn-h: UiStyle.size == 1 ? 34px : (UiStyle.size == 3 ? 52px : 42px);
|
||||
in-out property <length> btn-h-sm: UiStyle.size == 1 ? 28px : (UiStyle.size == 3 ? 40px : 32px);
|
||||
in-out property <length> mem-h: UiStyle.size == 1 ? 26px : (UiStyle.size == 3 ? 38px : 32px);
|
||||
in-out property <length> tab-h: UiStyle.size == 1 ? 26px : (UiStyle.size == 3 ? 34px : 30px);
|
||||
in-out property <length> font-sm: UiStyle.size == 1 ? 12px : (UiStyle.size == 3 ? 15px : 13px);
|
||||
in-out property <length> font-md: UiStyle.size == 1 ? 13px : (UiStyle.size == 3 ? 16px : 14px);
|
||||
in-out property <length> font-digit: UiStyle.size == 1 ? 14px : (UiStyle.size == 3 ? 18px : 16px);
|
||||
in-out property <length> font-eq: UiStyle.size == 1 ? 16px : (UiStyle.size == 3 ? 20px : 18px);
|
||||
}
|
||||
|
||||
component BitCell inherits Rectangle {
|
||||
callback toggled();
|
||||
in property <bool> on;
|
||||
width: 14px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background: touch.has-hover ? #3a4550 : transparent;
|
||||
background: touch.has-hover ? UiStyle.menu-active : transparent;
|
||||
|
||||
Text {
|
||||
text: root.on ? "1" : "0";
|
||||
color: root.on ? #60cdff : #6a6a6a;
|
||||
color: root.on ? UiStyle.accent : UiStyle.text-dim;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
font-family: "Cascadia Mono, Consolas, DejaVu Sans Mono, monospace";
|
||||
@@ -78,14 +107,14 @@ component BitLine inherits Rectangle {
|
||||
component MemBtn inherits Rectangle {
|
||||
callback clicked();
|
||||
in property <string> label;
|
||||
height: 32px;
|
||||
background: touch.has-hover ? #323232 : transparent;
|
||||
height: UiStyle.mem-h;
|
||||
background: touch.has-hover ? UiStyle.btn : transparent;
|
||||
border-radius: 4px;
|
||||
|
||||
Text {
|
||||
text: root.label;
|
||||
color: #c8c8c8;
|
||||
font-size: 13px;
|
||||
color: UiStyle.text-muted;
|
||||
font-size: UiStyle.font-sm;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
@@ -99,16 +128,16 @@ component FuncBtn inherits Rectangle {
|
||||
callback clicked();
|
||||
in property <string> label;
|
||||
in property <bool> enabled: true;
|
||||
in property <length> min-h: 42px;
|
||||
background: !root.enabled ? #2a2a2a : (touch.has-hover ? #464646 : #323232);
|
||||
in property <length> min-h: UiStyle.btn-h;
|
||||
background: !root.enabled ? UiStyle.surface : (touch.has-hover ? UiStyle.btn-hover : UiStyle.btn);
|
||||
border-radius: 4px;
|
||||
min-height: root.min-h;
|
||||
opacity: root.enabled ? 1.0 : 0.45;
|
||||
|
||||
Text {
|
||||
text: root.label;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
color: UiStyle.text;
|
||||
font-size: UiStyle.font-md;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
@@ -123,16 +152,16 @@ component DigitBtn inherits Rectangle {
|
||||
callback clicked();
|
||||
in property <string> label;
|
||||
in property <bool> enabled: true;
|
||||
in property <length> min-h: 42px;
|
||||
background: !root.enabled ? #2a2a2a : (touch.has-hover ? #505050 : #3b3b3b);
|
||||
in property <length> min-h: UiStyle.btn-h;
|
||||
background: !root.enabled ? UiStyle.surface : (touch.has-hover ? UiStyle.digit-hover : UiStyle.digit);
|
||||
border-radius: 4px;
|
||||
min-height: root.min-h;
|
||||
opacity: root.enabled ? 1.0 : 0.4;
|
||||
|
||||
Text {
|
||||
text: root.label;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
color: UiStyle.text;
|
||||
font-size: UiStyle.font-digit;
|
||||
font-weight: 600;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
@@ -146,15 +175,15 @@ component DigitBtn inherits Rectangle {
|
||||
|
||||
component EqBtn inherits Rectangle {
|
||||
callback clicked();
|
||||
in property <length> min-h: 42px;
|
||||
background: touch.has-hover ? #78d7ff : #60cdff;
|
||||
in property <length> min-h: UiStyle.btn-h;
|
||||
background: touch.has-hover ? UiStyle.accent-hover : UiStyle.accent;
|
||||
border-radius: 4px;
|
||||
min-height: root.min-h;
|
||||
|
||||
Text {
|
||||
text: "=";
|
||||
color: #141414;
|
||||
font-size: 18px;
|
||||
color: UiStyle.eq-text;
|
||||
font-size: UiStyle.font-eq;
|
||||
font-weight: 700;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
@@ -170,11 +199,11 @@ component MenuRow inherits Rectangle {
|
||||
in property <string> label;
|
||||
in property <bool> active;
|
||||
height: 36px;
|
||||
background: root.active ? #2f3a42 : (touch.has-hover ? #323232 : transparent);
|
||||
background: root.active ? UiStyle.menu-active : (touch.has-hover ? UiStyle.btn : transparent);
|
||||
|
||||
Text {
|
||||
text: root.label;
|
||||
color: root.active ? #60cdff : #e0e0e0;
|
||||
color: root.active ? UiStyle.accent : UiStyle.text;
|
||||
font-size: 14px;
|
||||
font-weight: root.active ? 700 : 500;
|
||||
vertical-alignment: center;
|
||||
@@ -193,14 +222,14 @@ component ModeTab inherits Rectangle {
|
||||
callback clicked();
|
||||
in property <string> label;
|
||||
in property <bool> active;
|
||||
height: 30px;
|
||||
height: UiStyle.tab-h;
|
||||
border-radius: 4px;
|
||||
background: root.active ? #3b3b3b : (touch.has-hover ? #2c2c2c : transparent);
|
||||
background: root.active ? UiStyle.btn : (touch.has-hover ? UiStyle.surface : transparent);
|
||||
|
||||
Text {
|
||||
text: root.label;
|
||||
color: root.active ? #60cdff : #c8c8c8;
|
||||
font-size: 13px;
|
||||
color: root.active ? UiStyle.accent : UiStyle.text-muted;
|
||||
font-size: UiStyle.font-sm;
|
||||
font-weight: root.active ? 700 : 500;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
@@ -325,12 +354,14 @@ export component AppWindow inherits Window {
|
||||
preferred-height: 640px;
|
||||
min-width: 380px;
|
||||
min-height: 520px;
|
||||
background: #202020;
|
||||
background: UiStyle.bg;
|
||||
|
||||
// 0=standard 1=engineering 2=programmer 3=convert 4=crc
|
||||
// 0=standard 1=engineering 2=programmer 3=convert 4=crc 5=settings
|
||||
in-out property <int> mode: 0;
|
||||
in-out property <bool> menu-open: false;
|
||||
in property <string> mode-title: "Standard";
|
||||
in property <int> cfg-button-size: 2;
|
||||
in property <int> cfg-theme: 0;
|
||||
|
||||
in property <string> display-text: "0";
|
||||
in property <string> expression-text: "";
|
||||
@@ -414,7 +445,7 @@ export component AppWindow inherits Window {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 4px;
|
||||
background: menu-touch.has-hover || root.menu-open ? #3b3b3b : transparent;
|
||||
background: menu-touch.has-hover || root.menu-open ? UiStyle.btn : transparent;
|
||||
|
||||
// Drawn icon — Unicode ☰ is missing in many Windows fonts.
|
||||
VerticalLayout {
|
||||
@@ -424,9 +455,9 @@ export component AppWindow inherits Window {
|
||||
padding-right: 8px;
|
||||
spacing: 4px;
|
||||
|
||||
Rectangle { height: 2px; background: #e8e8e8; border-radius: 1px; }
|
||||
Rectangle { height: 2px; background: #e8e8e8; border-radius: 1px; }
|
||||
Rectangle { height: 2px; background: #e8e8e8; border-radius: 1px; }
|
||||
Rectangle { height: 2px; background: UiStyle.text; border-radius: 1px; }
|
||||
Rectangle { height: 2px; background: UiStyle.text; border-radius: 1px; }
|
||||
Rectangle { height: 2px; background: UiStyle.text; border-radius: 1px; }
|
||||
}
|
||||
|
||||
menu-touch := TouchArea {
|
||||
@@ -444,14 +475,14 @@ export component AppWindow inherits Window {
|
||||
x: 0;
|
||||
y: 38px;
|
||||
width: 210px;
|
||||
height: 220px;
|
||||
height: 260px;
|
||||
close-on-click: true;
|
||||
|
||||
Rectangle {
|
||||
background: #2a2a2a;
|
||||
background: UiStyle.surface;
|
||||
border-radius: 6px;
|
||||
border-width: 1px;
|
||||
border-color: #404040;
|
||||
border-color: UiStyle.border;
|
||||
|
||||
VerticalLayout {
|
||||
padding: 6px;
|
||||
@@ -502,6 +533,15 @@ export component AppWindow inherits Window {
|
||||
mode-menu.close();
|
||||
}
|
||||
}
|
||||
MenuRow {
|
||||
label: "Settings";
|
||||
active: root.mode == 5;
|
||||
clicked => {
|
||||
root.press("mode:settings");
|
||||
root.menu-open = false;
|
||||
mode-menu.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,7 +549,7 @@ export component AppWindow inherits Window {
|
||||
|
||||
Text {
|
||||
text: root.mode-title;
|
||||
color: white;
|
||||
color: UiStyle.text;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
vertical-alignment: center;
|
||||
@@ -521,7 +561,7 @@ export component AppWindow inherits Window {
|
||||
height: 18px;
|
||||
Text {
|
||||
text: root.has-memory && (root.mode == 0 || root.mode == 1) ? "M" : "";
|
||||
color: #b4b4b4;
|
||||
color: UiStyle.text-muted;
|
||||
font-size: 12px;
|
||||
horizontal-alignment: left;
|
||||
vertical-alignment: center;
|
||||
@@ -529,13 +569,13 @@ export component AppWindow inherits Window {
|
||||
}
|
||||
Text {
|
||||
text: root.mode == 2 ? root.word-size-label : "";
|
||||
color: #60cdff;
|
||||
color: UiStyle.accent;
|
||||
font-size: 11px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
text: root.expression-text;
|
||||
color: #aaaaaa;
|
||||
color: UiStyle.text-dim;
|
||||
font-size: 13px;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
@@ -544,7 +584,7 @@ export component AppWindow inherits Window {
|
||||
|
||||
if root.mode == 0 || root.mode == 1 || root.mode == 2: Text {
|
||||
text: root.display-text;
|
||||
color: white;
|
||||
color: UiStyle.text;
|
||||
font-size: root.mode == 2 ? 28px : 34px;
|
||||
font-weight: 700;
|
||||
horizontal-alignment: right;
|
||||
@@ -1141,85 +1181,85 @@ export component AppWindow inherits Window {
|
||||
spacing: 4px;
|
||||
Row {
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "2nd●" : "2nd";
|
||||
clicked => { root.press("eng:2nd"); }
|
||||
}
|
||||
FuncBtn { min-h: 32px; label: "π"; clicked => { root.press("eng:pi"); } }
|
||||
FuncBtn { min-h: 32px; label: "e"; clicked => { root.press("eng:e"); } }
|
||||
FuncBtn { min-h: 32px; label: "C"; clicked => { root.press("C"); } }
|
||||
FuncBtn { min-h: 32px; label: "⌫"; clicked => { root.press("BS"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "π"; clicked => { root.press("eng:pi"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "e"; clicked => { root.press("eng:e"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "C"; clicked => { root.press("C"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "⌫"; clicked => { root.press("BS"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "sin⁻¹" : "sin";
|
||||
clicked => { root.press(root.eng-second ? "eng:asin" : "eng:sin"); }
|
||||
}
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "cos⁻¹" : "cos";
|
||||
clicked => { root.press(root.eng-second ? "eng:acos" : "eng:cos"); }
|
||||
}
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "tan⁻¹" : "tan";
|
||||
clicked => { root.press(root.eng-second ? "eng:atan" : "eng:tan"); }
|
||||
}
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "³√x" : "√x";
|
||||
clicked => { root.press(root.eng-second ? "eng:cbrt" : "eng:sqrt"); }
|
||||
}
|
||||
FuncBtn { min-h: 32px; label: "÷"; clicked => { root.press("/"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "÷"; clicked => { root.press("/"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "x³" : "x²";
|
||||
clicked => { root.press(root.eng-second ? "eng:cube" : "eng:sq"); }
|
||||
}
|
||||
FuncBtn { min-h: 32px; label: "1/x"; clicked => { root.press("eng:inv"); } }
|
||||
FuncBtn { min-h: 32px; label: "|x|"; clicked => { root.press("eng:abs"); } }
|
||||
FuncBtn { min-h: 32px; label: "n!"; clicked => { root.press("eng:fact"); } }
|
||||
FuncBtn { min-h: 32px; label: "×"; clicked => { root.press("*"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "1/x"; clicked => { root.press("eng:inv"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "|x|"; clicked => { root.press("eng:abs"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "n!"; clicked => { root.press("eng:fact"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "×"; clicked => { root.press("*"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn { min-h: 32px; label: "x^y"; clicked => { root.press("^"); } }
|
||||
FuncBtn { min-h: 32px; label: "("; clicked => { root.press("("); } }
|
||||
FuncBtn { min-h: 32px; label: ")"; clicked => { root.press(")"); } }
|
||||
FuncBtn { min-h: 32px; label: "CE"; clicked => { root.press("CE"); } }
|
||||
FuncBtn { min-h: 32px; label: "−"; clicked => { root.press("-"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "x^y"; clicked => { root.press("^"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "("; clicked => { root.press("("); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: ")"; clicked => { root.press(")"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "CE"; clicked => { root.press("CE"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "−"; clicked => { root.press("-"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn {
|
||||
min-h: 32px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.eng-second ? "eˣ" : "10ˣ";
|
||||
clicked => { root.press(root.eng-second ? "eng:exp" : "eng:tenpow"); }
|
||||
}
|
||||
DigitBtn { min-h: 32px; label: "7"; clicked => { root.press("7"); } }
|
||||
DigitBtn { min-h: 32px; label: "8"; clicked => { root.press("8"); } }
|
||||
DigitBtn { min-h: 32px; label: "9"; clicked => { root.press("9"); } }
|
||||
FuncBtn { min-h: 32px; label: "+"; clicked => { root.press("+"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "7"; clicked => { root.press("7"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "8"; clicked => { root.press("8"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "9"; clicked => { root.press("9"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "+"; clicked => { root.press("+"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn { min-h: 32px; label: "log"; clicked => { root.press("eng:log"); } }
|
||||
DigitBtn { min-h: 32px; label: "4"; clicked => { root.press("4"); } }
|
||||
DigitBtn { min-h: 32px; label: "5"; clicked => { root.press("5"); } }
|
||||
DigitBtn { min-h: 32px; label: "6"; clicked => { root.press("6"); } }
|
||||
DigitBtn { min-h: 32px; label: "±"; clicked => { root.press("neg"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "log"; clicked => { root.press("eng:log"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "4"; clicked => { root.press("4"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "5"; clicked => { root.press("5"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "6"; clicked => { root.press("6"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "±"; clicked => { root.press("neg"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn { min-h: 32px; label: "ln"; clicked => { root.press("eng:ln"); } }
|
||||
DigitBtn { min-h: 32px; label: "1"; clicked => { root.press("1"); } }
|
||||
DigitBtn { min-h: 32px; label: "2"; clicked => { root.press("2"); } }
|
||||
DigitBtn { min-h: 32px; label: "3"; clicked => { root.press("3"); } }
|
||||
EqBtn { min-h: 32px; clicked => { root.press("="); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "ln"; clicked => { root.press("eng:ln"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "1"; clicked => { root.press("1"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "2"; clicked => { root.press("2"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "3"; clicked => { root.press("3"); } }
|
||||
EqBtn { min-h: UiStyle.btn-h-sm; clicked => { root.press("="); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 32px; label: "0"; clicked => { root.press("0"); } }
|
||||
DigitBtn { min-h: 32px; label: "."; clicked => { root.press("."); } }
|
||||
FuncBtn { min-h: 32px; label: "exp"; clicked => { root.press("eng:exp"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "0"; clicked => { root.press("0"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "."; clicked => { root.press("."); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "exp"; clicked => { root.press("eng:exp"); } }
|
||||
Rectangle { min-height: 32px; background: transparent; }
|
||||
Rectangle { min-height: 32px; background: transparent; }
|
||||
}
|
||||
@@ -1295,62 +1335,136 @@ export component AppWindow inherits Window {
|
||||
if root.mode == 2: GridLayout {
|
||||
spacing: 4px;
|
||||
Row {
|
||||
FuncBtn { min-h: 34px; label: root.word-size-label; clicked => { root.press("word"); } }
|
||||
FuncBtn { min-h: 34px; label: "CE"; clicked => { root.press("CE"); } }
|
||||
FuncBtn { min-h: 34px; label: "C"; clicked => { root.press("clear"); } }
|
||||
FuncBtn { min-h: 34px; label: "⌫"; clicked => { root.press("BS"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: root.word-size-label; clicked => { root.press("word"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "CE"; clicked => { root.press("CE"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "C"; clicked => { root.press("clear"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "⌫"; clicked => { root.press("BS"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn { min-h: 34px; label: "AND"; clicked => { root.press("AND"); } }
|
||||
FuncBtn { min-h: 34px; label: "OR"; clicked => { root.press("OR"); } }
|
||||
FuncBtn { min-h: 34px; label: "NOT"; clicked => { root.press("NOT"); } }
|
||||
FuncBtn { min-h: 34px; label: "NAND"; clicked => { root.press("NAND"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "AND"; clicked => { root.press("AND"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "OR"; clicked => { root.press("OR"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "NOT"; clicked => { root.press("NOT"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "NAND"; clicked => { root.press("NAND"); } }
|
||||
}
|
||||
Row {
|
||||
FuncBtn { min-h: 34px; label: "NOR"; clicked => { root.press("NOR"); } }
|
||||
FuncBtn { min-h: 34px; label: "XOR"; clicked => { root.press("XOR"); } }
|
||||
FuncBtn { min-h: 34px; label: "Lsh"; clicked => { root.press("Lsh"); } }
|
||||
FuncBtn { min-h: 34px; label: "Rsh"; clicked => { root.press("Rsh"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "NOR"; clicked => { root.press("NOR"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "XOR"; clicked => { root.press("XOR"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "Lsh"; clicked => { root.press("Lsh"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "Rsh"; clicked => { root.press("Rsh"); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 34px; label: "A"; enabled: root.allow-a; clicked => { root.press("A"); } }
|
||||
DigitBtn { min-h: 34px; label: "B"; enabled: root.allow-a; clicked => { root.press("B"); } }
|
||||
DigitBtn { min-h: 34px; label: "C"; enabled: root.allow-a; clicked => { root.press("C"); } }
|
||||
FuncBtn { min-h: 34px; label: "÷"; clicked => { root.press("/"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "A"; enabled: root.allow-a; clicked => { root.press("A"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "B"; enabled: root.allow-a; clicked => { root.press("B"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "C"; enabled: root.allow-a; clicked => { root.press("C"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "÷"; clicked => { root.press("/"); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 34px; label: "D"; enabled: root.allow-a; clicked => { root.press("D"); } }
|
||||
DigitBtn { min-h: 34px; label: "E"; enabled: root.allow-a; clicked => { root.press("E"); } }
|
||||
DigitBtn { min-h: 34px; label: "F"; enabled: root.allow-a; clicked => { root.press("F"); } }
|
||||
FuncBtn { min-h: 34px; label: "×"; clicked => { root.press("*"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "D"; enabled: root.allow-a; clicked => { root.press("D"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "E"; enabled: root.allow-a; clicked => { root.press("E"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "F"; enabled: root.allow-a; clicked => { root.press("F"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "×"; clicked => { root.press("*"); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 34px; label: "7"; enabled: root.allow-2; clicked => { root.press("7"); } }
|
||||
DigitBtn { min-h: 34px; label: "8"; enabled: root.allow-8; clicked => { root.press("8"); } }
|
||||
DigitBtn { min-h: 34px; label: "9"; enabled: root.allow-8; clicked => { root.press("9"); } }
|
||||
FuncBtn { min-h: 34px; label: "−"; clicked => { root.press("-"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "7"; enabled: root.allow-2; clicked => { root.press("7"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "8"; enabled: root.allow-8; clicked => { root.press("8"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "9"; enabled: root.allow-8; clicked => { root.press("9"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "−"; clicked => { root.press("-"); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 34px; label: "4"; enabled: root.allow-2; clicked => { root.press("4"); } }
|
||||
DigitBtn { min-h: 34px; label: "5"; enabled: root.allow-2; clicked => { root.press("5"); } }
|
||||
DigitBtn { min-h: 34px; label: "6"; enabled: root.allow-2; clicked => { root.press("6"); } }
|
||||
FuncBtn { min-h: 34px; label: "+"; clicked => { root.press("+"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "4"; enabled: root.allow-2; clicked => { root.press("4"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "5"; enabled: root.allow-2; clicked => { root.press("5"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "6"; enabled: root.allow-2; clicked => { root.press("6"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "+"; clicked => { root.press("+"); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 34px; label: "1"; clicked => { root.press("1"); } }
|
||||
DigitBtn { min-h: 34px; label: "2"; enabled: root.active-base != 3; clicked => { root.press("2"); } }
|
||||
DigitBtn { min-h: 34px; label: "3"; enabled: root.allow-2; clicked => { root.press("3"); } }
|
||||
FuncBtn { min-h: 34px; label: "%"; clicked => { root.press("%"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "1"; clicked => { root.press("1"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "2"; enabled: root.active-base != 3; clicked => { root.press("2"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "3"; enabled: root.allow-2; clicked => { root.press("3"); } }
|
||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "%"; clicked => { root.press("%"); } }
|
||||
}
|
||||
Row {
|
||||
DigitBtn { min-h: 34px; label: "±"; clicked => { root.press("neg"); } }
|
||||
DigitBtn { min-h: 34px; label: "0"; clicked => { root.press("0"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "±"; clicked => { root.press("neg"); } }
|
||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "0"; clicked => { root.press("0"); } }
|
||||
FuncBtn {
|
||||
min-h: 34px;
|
||||
min-h: UiStyle.btn-h-sm;
|
||||
label: root.show-bits ? "Bits●" : "Bits";
|
||||
clicked => { root.press("bits"); }
|
||||
}
|
||||
EqBtn { min-h: 34px; clicked => { root.press("="); } }
|
||||
EqBtn { min-h: UiStyle.btn-h-sm; clicked => { root.press("="); } }
|
||||
}
|
||||
}
|
||||
|
||||
if root.mode == 5: VerticalLayout {
|
||||
spacing: 14px;
|
||||
padding-top: 8px;
|
||||
|
||||
Text {
|
||||
text: "Appearance";
|
||||
color: UiStyle.accent;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Button size";
|
||||
color: UiStyle.text-dim;
|
||||
font-size: 12px;
|
||||
}
|
||||
HorizontalLayout {
|
||||
spacing: 6px;
|
||||
ModeTab {
|
||||
label: "1 Compact";
|
||||
active: root.cfg-button-size == 1;
|
||||
clicked => { root.press("cfg:size:1"); }
|
||||
}
|
||||
ModeTab {
|
||||
label: "2 Normal";
|
||||
active: root.cfg-button-size == 2;
|
||||
clicked => { root.press("cfg:size:2"); }
|
||||
}
|
||||
ModeTab {
|
||||
label: "3 Large";
|
||||
active: root.cfg-button-size == 3;
|
||||
clicked => { root.press("cfg:size:3"); }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Theme";
|
||||
color: UiStyle.text-dim;
|
||||
font-size: 12px;
|
||||
}
|
||||
HorizontalLayout {
|
||||
spacing: 6px;
|
||||
ModeTab {
|
||||
label: "Dark";
|
||||
active: root.cfg-theme == 0;
|
||||
clicked => { root.press("cfg:theme:0"); }
|
||||
}
|
||||
ModeTab {
|
||||
label: "Light";
|
||||
active: root.cfg-theme == 1;
|
||||
clicked => { root.press("cfg:theme:1"); }
|
||||
}
|
||||
ModeTab {
|
||||
label: "Blue";
|
||||
active: root.cfg-theme == 2;
|
||||
clicked => { root.press("cfg:theme:2"); }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Settings are saved automatically.";
|
||||
color: UiStyle.text-dim;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: 6px;
|
||||
FuncBtn {
|
||||
label: "Back";
|
||||
clicked => { root.press("mode:std"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user