0.1.20
This commit is contained in:
+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();
|
||||
|
||||
Reference in New Issue
Block a user