sideleft: new tool: conversions

This commit is contained in:
end-4
2025-03-29 13:04:32 +01:00
parent f2943f766a
commit 4f5d4f802e
5 changed files with 207 additions and 0 deletions
@@ -1,4 +1,10 @@
export function clamp(x, min, max) {
return Math.min(Math.max(x, min), max);
}
export function truncateToPrecision(value, precision) {
const factor = Math.pow(10, precision);
const result = Math.round(value * factor) / factor;
return result;
}
+2
View File
@@ -2,6 +2,7 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
const { Box, Label, Scrollable } = Widget;
import QuickScripts from './tools/quickscripts.js';
import ColorPicker from './tools/colorpicker.js';
import Conversions from './tools/conversions.js';
import Name from './tools/name.js';
export default Scrollable({
@@ -12,6 +13,7 @@ export default Scrollable({
className: 'spacing-v-10',
children: [
QuickScripts(),
Conversions(),
ColorPicker(),
Box({ vexpand: true }),
Name(),
@@ -0,0 +1,169 @@
const { Gtk } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { execAsync, exec } = Utils;
const { Box, Button, Entry, EventBox, Icon, Label, Scrollable, Overlay } = Widget;
import SidebarModule from './module.js';
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import { setupCursorHover } from '../../.widgetutils/cursorhover.js';
import { truncateToPrecision } from '../../.miscutils/mathfuncs.js';
const VALUE_DEFAULT_PRECISION = 3;
const conversions = [
{
unit1: 'px',
unit2: 'rem',
unit1Default: 5,
formula1to2: '{{x}} / (parseFloat(Utils.exec(\'gsettings get org.gnome.desktop.interface font-name\').split(" ")[1].split("\'"))*4/3)',
formula2to1: '{{x}} * 3 / 4',
forcePrecision: true,
},
{
unit1: 'degrees',
unit2: 'radians',
unit1Default: 90,
formula1to2: '{{x}} * Math.PI / 180',
formula2to1: '{{x}} * 180 / Math.PI',
},
{
unit1: '°F',
unit2: '°C',
unit1Default: 68,
formula1to2: '({{x}} - 32) * 5 / 9',
formula2to1: '{{x}} * 9 / 5 + 32',
},
{
unit1: 'Ft',
unit2: 'Cm',
formula1to2: '{{x}} * 30.48',
formula2to1: '{{x}} / 30.48',
},
// {
// unit1: 'Mile',
// unit2: 'Km',
// formula1to2: '{{x}} * 1.60934',
// formula2to1: '{{x}} / 1.60934',
// },
// {
// unit1: 'Inch',
// unit2: 'Cm',
// formula1to2: '{{x}} * 2.54',
// formula2to1: '{{x}} / 2.54',
// },
{
unit1: 'lbs',
unit2: 'Kg',
formula1to2: '{{x}} * 0.453592',
formula2to1: '{{x}} / 0.453592',
}
]
export default () => {
const ValueBox = ({ unit, initValue = 0, updateCallback }) => {
const unitName = Label({
xalign: 0,
className: 'txt txt-smallie',
label: `${unit}`,
});
const entry = Entry({
hexpand: 'true',
widthChars: 10,
className: 'txt-small techfont',
text: `${initValue}`,
onChange: updateCallback,
});
const copyButton = Button({
className: 'sidebar-module-csscalc-valuebox-copybtn',
child: MaterialIcon('content_copy', 'norm'),
onClicked: (self) => {
Utils.execAsync(['wl-copy', entry.text]);
self.child.label = 'done';
Utils.timeout(1000, () => self.child.label = 'content_copy');
},
setup: setupCursorHover,
});
const wholeThing = Box({
className: 'sidebar-module-csscalc-valuebox',
vertical: true,
hexpand: true,
children: [
unitName,
Box({
children: [
entry,
copyButton,
]
})
],
attribute: {
updateValue: (value) => entry.text = `${value}`,
getValue: () => entry.text,
}
});
return wholeThing;
}
// Formula format is js expression, with `{{x}}` being the input value
const BidirectionalConversion = ({
unit1, unit2, unit1Default = 1,
formula1to2, formula2to1,
forcePrecision = false, precision = VALUE_DEFAULT_PRECISION,
}) => {
let updateLock = false;
const convert = (value, formula) => {
let thisValue;
try {
thisValue = eval(value)
} catch (error) {
thisValue = parseFloat(value);
}
// print(formula.replace('{{x}}', thisValue))
// print(eval(formula.replace('{{x}}', thisValue)))
const evalResult = eval(formula.replace('{{x}}', thisValue));
const result = forcePrecision ?
evalResult.toFixed(precision) : truncateToPrecision(evalResult, precision);
// print(result)
return result;
}
const unit1Box = ValueBox({
unit: unit1,
initValue: unit1Default,
updateCallback: (self) => {
if (updateLock) return;
updateLock = true;
const newValue = convert(self.text, formula1to2);
unit2Box.attribute.updateValue(newValue || 0);
updateLock = false;
},
});
const unit2Box = ValueBox({
unit: unit2,
initValue: truncateToPrecision(eval(formula1to2.replace('\{{x}}', unit1Default)), precision),
updateCallback: (self) => {
if (updateLock) return;
updateLock = true;
const newValue = convert(self.text, formula2to1);
unit1Box.attribute.updateValue(newValue || 0);
updateLock = false;
},
});
return Box({
className: 'txt spacing-h-10',
children: [
unit1Box,
MaterialIcon('swap_horiz', 'large'),
unit2Box,
]
})
}
return SidebarModule({
icon: MaterialIcon('autorenew', 'norm'),
name: getString('Conversions'),
child: Box({
vertical: true,
className: 'spacing-v-5',
children: conversions.map(BidirectionalConversion),
})
});
}
+5
View File
@@ -14,6 +14,11 @@ $rounding_large: 1.705rem;
border-radius: $rounding_unsharpen;
}
@mixin verysmall-rounding {
border-radius: $rounding_verysmall;
-gtk-outline-radius: $rounding_verysmall;
}
@mixin small-rounding {
border-radius: $rounding_small;
-gtk-outline-radius: $rounding_small;
+25
View File
@@ -510,6 +510,31 @@ $colorpicker_rounding: 0.341rem;
padding: 0.341rem;
}
.sidebar-module-csscalc-valuebox {
@include small-rounding;
padding: 0.341rem;
background-color: $layer2;
color: $onLayer2;
}
.sidebar-module-csscalc-valuebox-copybtn {
@include verysmall-rounding;
@include element_decel;
min-width: 1.705rem;
min-height: 1.705rem;
background-color: $layer2;
color: $onLayer2;
&:hover,
&:focus {
background-color: $layer2Hover;
}
&:active {
background-color: $layer2Active;
}
}
.sidebar-icontabswitcher {
@include full-rounding;
@include group-padding;