300826
This commit is contained in:
Generated
+1
-1
@@ -3904,7 +3904,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rcalc"
|
name = "rcalc"
|
||||||
version = "0.1.25"
|
version = "0.1.28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"slint",
|
"slint",
|
||||||
"slint-build",
|
"slint-build",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rcalc"
|
name = "rcalc"
|
||||||
version = "0.1.25"
|
version = "0.1.28"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let version = resolve_app_version();
|
let version = resolve_app_version();
|
||||||
@@ -38,82 +35,10 @@ fn embed_windows_icon() {
|
|||||||
|
|
||||||
/// Window / package version shown as `Rcalc-{version}`.
|
/// Window / package version shown as `Rcalc-{version}`.
|
||||||
///
|
///
|
||||||
/// On each `release` build the patch in `Cargo.toml` is incremented once
|
/// Always the version from `Cargo.toml`. Раньше каждая release-сборка
|
||||||
/// (`0.1.0` → `0.1.1` → …). If Cargo restarts the build script right after we
|
/// инкрементировала patch в Cargo.toml посреди сборки — из-за этого
|
||||||
/// rewrite `Cargo.toml`, a short mtime guard prevents a double bump.
|
/// имя AppImage (читается скриптом до сборки) отставало от версии,
|
||||||
|
/// зашитой в бинарник. Теперь версия меняется только вручную.
|
||||||
fn resolve_app_version() -> String {
|
fn resolve_app_version() -> String {
|
||||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.0.0".into())
|
||||||
let pkg = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.0.0".into());
|
|
||||||
let profile = env::var("PROFILE").unwrap_or_default();
|
|
||||||
|
|
||||||
if profile != "release" {
|
|
||||||
return pkg;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cargo_toml = manifest_dir.join("Cargo.toml");
|
|
||||||
let guard_path = manifest_dir.join(".rcalc_last_bump");
|
|
||||||
let already = fs::read_to_string(&guard_path)
|
|
||||||
.map(|s| s.trim().to_string())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Cargo re-invoked us because we just rewrote Cargo.toml — keep version.
|
|
||||||
if already == pkg {
|
|
||||||
if let Ok(meta) = fs::metadata(&cargo_toml) {
|
|
||||||
if let Ok(modified) = meta.modified() {
|
|
||||||
if modified.elapsed().unwrap_or(Duration::MAX) < Duration::from_secs(15) {
|
|
||||||
return pkg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(next) = bump_patch(&pkg) else {
|
|
||||||
return pkg;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = set_cargo_toml_version(&cargo_toml, &next) {
|
|
||||||
println!("cargo:warning=could not bump Cargo.toml version: {e}");
|
|
||||||
return pkg;
|
|
||||||
}
|
|
||||||
let _ = fs::write(&guard_path, &next);
|
|
||||||
next
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bump_patch(version: &str) -> Option<String> {
|
|
||||||
let mut parts: Vec<&str> = version.split('.').collect();
|
|
||||||
if parts.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
while parts.len() < 3 {
|
|
||||||
parts.push("0");
|
|
||||||
}
|
|
||||||
let patch: u64 = parts[2]
|
|
||||||
.split(['-', '+'])
|
|
||||||
.next()?
|
|
||||||
.parse()
|
|
||||||
.ok()?;
|
|
||||||
Some(format!("{}.{}.{}", parts[0], parts[1], patch + 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_cargo_toml_version(path: &PathBuf, version: &str) -> std::io::Result<()> {
|
|
||||||
let raw = fs::read_to_string(path)?;
|
|
||||||
let mut out = String::with_capacity(raw.len() + 8);
|
|
||||||
let mut replaced = false;
|
|
||||||
for line in raw.lines() {
|
|
||||||
if !replaced && line.starts_with("version") && line.contains('=') {
|
|
||||||
out.push_str(&format!("version = \"{version}\""));
|
|
||||||
out.push('\n');
|
|
||||||
replaced = true;
|
|
||||||
} else {
|
|
||||||
out.push_str(line);
|
|
||||||
out.push('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !replaced {
|
|
||||||
return Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidData,
|
|
||||||
"version field not found in Cargo.toml",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
fs::write(path, out)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -101,6 +101,9 @@ package_appimage() {
|
|||||||
echo "Bundling libraries with linuxdeploy…"
|
echo "Bundling libraries with linuxdeploy…"
|
||||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||||
export OUTPUT="${IMAGE_NAME}"
|
export OUTPUT="${IMAGE_NAME}"
|
||||||
|
# Marker: mtime boundary to find the FRESH linuxdeploy output.
|
||||||
|
local marker="${OUT_DIR}/.pack-start"
|
||||||
|
touch "${marker}"
|
||||||
(
|
(
|
||||||
cd "${OUT_DIR}"
|
cd "${OUT_DIR}"
|
||||||
"${linuxdeploy}" \
|
"${linuxdeploy}" \
|
||||||
@@ -111,11 +114,15 @@ package_appimage() {
|
|||||||
--output appimage
|
--output appimage
|
||||||
)
|
)
|
||||||
|
|
||||||
# linuxdeploy drops the AppImage next to AppDir (dist/)
|
# linuxdeploy drops the AppImage next to AppDir (dist/). Only consider files
|
||||||
|
# NEWER than the marker: a blind `find | head -1` with several older
|
||||||
|
# *.AppImage in dist/ renamed an OLD image OVER the freshly built one
|
||||||
|
# (rcalc-0.1.28 got shipped with a 0.1.25 binary from the first build).
|
||||||
local built
|
local built
|
||||||
built="$(find "${OUT_DIR}" -maxdepth 1 -name '*.AppImage' ! -name 'linuxdeploy*' | head -1)"
|
built="$(find "${OUT_DIR}" -maxdepth 1 -name '*.AppImage' ! -name 'linuxdeploy*' -newer "${marker}" | head -1)"
|
||||||
|
rm -f "${marker}"
|
||||||
if [[ -z "$built" ]]; then
|
if [[ -z "$built" ]]; then
|
||||||
echo "error: AppImage was not produced" >&2
|
echo "error: AppImage was not produced (linuxdeploy failed?)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ "$(basename "$built")" != "${IMAGE_NAME}" ]]; then
|
if [[ "$(basename "$built")" != "${IMAGE_NAME}" ]]; then
|
||||||
|
|||||||
@@ -389,6 +389,26 @@ impl Engineering {
|
|||||||
self.sync_expr();
|
self.sync_expr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert `× 10 ^` for scientific-notation entry: `1.5 [×10ˣ] 3 = 1500`.
|
||||||
|
/// Exponent may be negated with ± before `=` (`10 ^ −3` parses correctly).
|
||||||
|
pub fn mul_pow10(&mut self) {
|
||||||
|
if self.error.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.done {
|
||||||
|
// Continue from the result: 1500 [×10ˣ] 2 = 150000.
|
||||||
|
self.formula.clear();
|
||||||
|
self.done = false;
|
||||||
|
self.typing = true;
|
||||||
|
}
|
||||||
|
self.flush_entry();
|
||||||
|
self.formula.push_str(" × 10 ^ ");
|
||||||
|
self.entry = "0".into();
|
||||||
|
self.typing = false;
|
||||||
|
self.second = false;
|
||||||
|
self.sync_expr();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn equals(&mut self) {
|
pub fn equals(&mut self) {
|
||||||
if self.error.is_some() {
|
if self.error.is_some() {
|
||||||
return;
|
return;
|
||||||
@@ -884,6 +904,47 @@ mod tests {
|
|||||||
assert_eq!(e.display(), "1024");
|
assert_eq!(e.display(), "1024");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mul_pow10_basic() {
|
||||||
|
// 1.5 ×10ˣ 3 = 1500 (× 10 ^ с приоритетом ^ над ×).
|
||||||
|
let mut e = Engineering::new();
|
||||||
|
e.input_digit('1');
|
||||||
|
e.input_dot();
|
||||||
|
e.input_digit('5');
|
||||||
|
e.mul_pow10();
|
||||||
|
e.input_digit('3');
|
||||||
|
e.equals();
|
||||||
|
assert_eq!(e.display(), "1500");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mul_pow10_negative_exponent() {
|
||||||
|
// 2.5 ×10ˣ ±2 = 0.025.
|
||||||
|
let mut e = Engineering::new();
|
||||||
|
e.input_digit('2');
|
||||||
|
e.input_dot();
|
||||||
|
e.input_digit('5');
|
||||||
|
e.mul_pow10();
|
||||||
|
e.input_digit('2');
|
||||||
|
e.negate();
|
||||||
|
e.equals();
|
||||||
|
assert_eq!(e.display(), "0.025");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mul_pow10_continues_result() {
|
||||||
|
// 2 × 3 = 6, затем 6 ×10ˣ 2 = 600.
|
||||||
|
let mut e = Engineering::new();
|
||||||
|
e.input_digit('2');
|
||||||
|
e.set_op("×");
|
||||||
|
e.input_digit('3');
|
||||||
|
e.equals();
|
||||||
|
e.mul_pow10();
|
||||||
|
e.input_digit('2');
|
||||||
|
e.equals();
|
||||||
|
assert_eq!(e.display(), "600");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ten_pow_negative_not_zero() {
|
fn ten_pow_negative_not_zero() {
|
||||||
// 10 ^ −50 ≈ 1e-50 was snapped to "0" by format_num.
|
// 10 ^ −50 ≈ 1e-50 was snapped to "0" by format_num.
|
||||||
|
|||||||
@@ -532,6 +532,7 @@ fn handle_engineering(calc: &mut Engineering, id: &str) {
|
|||||||
"eng:fact" => calc.insert_func("fact"),
|
"eng:fact" => calc.insert_func("fact"),
|
||||||
"eng:exp" => calc.insert_func("exp"),
|
"eng:exp" => calc.insert_func("exp"),
|
||||||
"eng:tenpow" => calc.insert_func("tenpow"),
|
"eng:tenpow" => calc.insert_func("tenpow"),
|
||||||
|
"eng:mul10pow" => calc.mul_pow10(),
|
||||||
"eng:log" => calc.insert_func("log"),
|
"eng:log" => calc.insert_func("log"),
|
||||||
"eng:ln" => calc.insert_func("ln"),
|
"eng:ln" => calc.insert_func("ln"),
|
||||||
"MC" => calc.memory_clear(),
|
"MC" => calc.memory_clear(),
|
||||||
|
|||||||
+19
-12
@@ -1211,7 +1211,7 @@ export component AppWindow inherits Window {
|
|||||||
label: root.eng-second ? "³√x" : "√x";
|
label: root.eng-second ? "³√x" : "√x";
|
||||||
clicked => { root.press(root.eng-second ? "eng:cbrt" : "eng:sqrt"); }
|
clicked => { root.press(root.eng-second ? "eng:cbrt" : "eng:sqrt"); }
|
||||||
}
|
}
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "÷"; clicked => { root.press("/"); } }
|
FuncBtn { min-h: UiStyle.btn-h-sm; label: "x*10^y"; clicked => { root.press("eng:mul10pow"); } }
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
FuncBtn {
|
FuncBtn {
|
||||||
@@ -1222,14 +1222,14 @@ export component AppWindow inherits Window {
|
|||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "1/x"; clicked => { root.press("eng:inv"); } }
|
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: "|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: "n!"; clicked => { root.press("eng:fact"); } }
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "×"; clicked => { root.press("*"); } }
|
FuncBtn { min-h: UiStyle.btn-h-sm; label: "÷"; clicked => { root.press("/"); } }
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "x^y"; 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: ")"; 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: "CE"; clicked => { root.press("CE"); } }
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "−"; clicked => { root.press("-"); } }
|
FuncBtn { min-h: UiStyle.btn-h-sm; label: "×"; clicked => { root.press("*"); } }
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
FuncBtn {
|
FuncBtn {
|
||||||
@@ -1240,28 +1240,28 @@ export component AppWindow inherits Window {
|
|||||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "7"; clicked => { root.press("7"); } }
|
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: "8"; clicked => { root.press("8"); } }
|
||||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "9"; clicked => { root.press("9"); } }
|
DigitBtn { min-h: UiStyle.btn-h-sm; label: "9"; clicked => { root.press("9"); } }
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "+"; clicked => { root.press("+"); } }
|
FuncBtn { min-h: UiStyle.btn-h-sm; label: "−"; clicked => { root.press("-"); } }
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "log"; clicked => { root.press("eng:log"); } }
|
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: "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: "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: "6"; clicked => { root.press("6"); } }
|
||||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "±"; clicked => { root.press("neg"); } }
|
FuncBtn { min-h: UiStyle.btn-h-sm; label: "+"; clicked => { root.press("+"); } }
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
FuncBtn { min-h: UiStyle.btn-h-sm; label: "ln"; clicked => { root.press("eng:ln"); } }
|
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: "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: "2"; clicked => { root.press("2"); } }
|
||||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "3"; clicked => { root.press("3"); } }
|
DigitBtn { min-h: UiStyle.btn-h-sm; label: "3"; clicked => { root.press("3"); } }
|
||||||
EqBtn { min-h: UiStyle.btn-h-sm; clicked => { root.press("="); } }
|
DigitBtn { min-h: UiStyle.btn-h-sm; label: "±"; clicked => { root.press("neg"); } }
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
|
FuncBtn { min-h: UiStyle.btn-h-sm; 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: "0"; clicked => { root.press("0"); } }
|
||||||
DigitBtn { min-h: UiStyle.btn-h-sm; label: "."; clicked => { root.press("."); } }
|
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; }
|
Rectangle { min-height: 32px; background: transparent; }
|
||||||
|
EqBtn { min-h: UiStyle.btn-h-sm; clicked => { root.press("="); } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,10 +1293,17 @@ export component AppWindow inherits Window {
|
|||||||
if root.mode == 0: GridLayout {
|
if root.mode == 0: GridLayout {
|
||||||
spacing: 6px;
|
spacing: 6px;
|
||||||
Row {
|
Row {
|
||||||
if root.std-panel == 0: FuncBtn { label: "%"; clicked => { root.press("%"); } }
|
// `if` внутри Row у GridLayout ломает раскладку Slint
|
||||||
if root.std-panel == 1: FuncBtn { label: "("; clicked => { root.press("("); } }
|
// (RepeatedItemTree::grid_layout_input_data) — кнопки теряются.
|
||||||
if root.std-panel == 0: FuncBtn { label: "CE"; clicked => { root.press("CE"); } }
|
// Поэтому одна кнопка с тернарником вместо пары условных.
|
||||||
if root.std-panel == 1: FuncBtn { label: ")"; clicked => { root.press(")"); } }
|
FuncBtn {
|
||||||
|
label: root.std-panel == 0 ? "%" : "(";
|
||||||
|
clicked => { root.press(root.std-panel == 0 ? "%" : "("); }
|
||||||
|
}
|
||||||
|
FuncBtn {
|
||||||
|
label: root.std-panel == 0 ? "CE" : ")";
|
||||||
|
clicked => { root.press(root.std-panel == 0 ? "CE" : ")"); }
|
||||||
|
}
|
||||||
FuncBtn { label: "C"; clicked => { root.press("C"); } }
|
FuncBtn { label: "C"; clicked => { root.press("C"); } }
|
||||||
FuncBtn { label: "⌫"; clicked => { root.press("BS"); } }
|
FuncBtn { label: "⌫"; clicked => { root.press("BS"); } }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user