forked from Shinonome/dots-hyprland
sideleft: new tool: conversions
This commit is contained in:
@@ -1,4 +1,10 @@
|
|||||||
|
|
||||||
export function clamp(x, min, max) {
|
export function clamp(x, min, max) {
|
||||||
return Math.min(Math.max(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,6 +2,7 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|||||||
const { Box, Label, Scrollable } = Widget;
|
const { Box, Label, Scrollable } = Widget;
|
||||||
import QuickScripts from './tools/quickscripts.js';
|
import QuickScripts from './tools/quickscripts.js';
|
||||||
import ColorPicker from './tools/colorpicker.js';
|
import ColorPicker from './tools/colorpicker.js';
|
||||||
|
import Conversions from './tools/conversions.js';
|
||||||
import Name from './tools/name.js';
|
import Name from './tools/name.js';
|
||||||
|
|
||||||
export default Scrollable({
|
export default Scrollable({
|
||||||
@@ -12,6 +13,7 @@ export default Scrollable({
|
|||||||
className: 'spacing-v-10',
|
className: 'spacing-v-10',
|
||||||
children: [
|
children: [
|
||||||
QuickScripts(),
|
QuickScripts(),
|
||||||
|
Conversions(),
|
||||||
ColorPicker(),
|
ColorPicker(),
|
||||||
Box({ vexpand: true }),
|
Box({ vexpand: true }),
|
||||||
Name(),
|
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),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -14,6 +14,11 @@ $rounding_large: 1.705rem;
|
|||||||
border-radius: $rounding_unsharpen;
|
border-radius: $rounding_unsharpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin verysmall-rounding {
|
||||||
|
border-radius: $rounding_verysmall;
|
||||||
|
-gtk-outline-radius: $rounding_verysmall;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin small-rounding {
|
@mixin small-rounding {
|
||||||
border-radius: $rounding_small;
|
border-radius: $rounding_small;
|
||||||
-gtk-outline-radius: $rounding_small;
|
-gtk-outline-radius: $rounding_small;
|
||||||
|
|||||||
@@ -510,6 +510,31 @@ $colorpicker_rounding: 0.341rem;
|
|||||||
padding: 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 {
|
.sidebar-icontabswitcher {
|
||||||
@include full-rounding;
|
@include full-rounding;
|
||||||
@include group-padding;
|
@include group-padding;
|
||||||
|
|||||||
Reference in New Issue
Block a user