Unifies the interface elements for measuring and constraining real-valued observables, as proposed in issue #47. The resulting combination is called a "Regulator," at least in the code. They are presented as text inputs in the table view. When a Regulatore is in measurement mode (has no "set point"), the text field displays its value. Entering a desired value into the text field creates a set point, and then the Regulator acts to (attempt to) constrain the value. Setting the desired value to the empty string switches the observable back to measurement mode. If you enter a desired value that can't be parsed as a floating point number, the regulator input is flagged as invalid and it has no effect on the state of the regulator. The set point can in this case be restored to its previous value (or to no set point if that was its prior state) by pressing the "Esc" key. Co-authored-by: Aaron Fenyes <aaron.fenyes@fareycircles.ooo> Co-authored-by: glen <glen@studioinfinity.org> Reviewed-on: glen/dyna3#48 Co-authored-by: Vectornaut <vectornaut@nobody@nowhere.net> Co-committed-by: Vectornaut <vectornaut@nobody@nowhere.net>
190 lines
No EOL
3.1 KiB
CSS
190 lines
No EOL
3.1 KiB
CSS
:root {
|
|
--text: #fcfcfc; /* almost white */
|
|
--text-bright: white;
|
|
--text-invalid: #f58fc2; /* bright pink */
|
|
--border: #555; /* light gray */
|
|
--border-focus-dark: #aaa; /* bright gray */
|
|
--border-focus-light: white;
|
|
--border-invalid: #70495c; /* dusky pink */
|
|
--selection-highlight: #444; /* medium gray */
|
|
--page-background: #222; /* dark gray */
|
|
--display-background: #020202; /* almost black */
|
|
}
|
|
|
|
body {
|
|
margin: 0px;
|
|
color: var(--text);
|
|
background-color: var(--page-background);
|
|
font-family: 'Fira Sans', sans-serif;
|
|
}
|
|
|
|
/* sidebar */
|
|
|
|
#sidebar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
float: left;
|
|
width: 500px;
|
|
height: 100vh;
|
|
margin: 0px;
|
|
padding: 0px;
|
|
border-width: 0px 1px 0px 0px;
|
|
border-style: solid;
|
|
border-color: var(--border);
|
|
}
|
|
|
|
/* add-remove */
|
|
|
|
#add-remove {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin: 8px;
|
|
}
|
|
|
|
#add-remove > button {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: large;
|
|
}
|
|
|
|
/* KLUDGE */
|
|
/*
|
|
for convenience, we're using emoji as temporary icons for some buttons. these
|
|
buttons need to be displayed in an emoji font
|
|
*/
|
|
#add-remove > button.emoji {
|
|
font-family: 'Noto Emoji', sans-serif;
|
|
}
|
|
|
|
/* outline */
|
|
|
|
#outline {
|
|
flex-grow: 1;
|
|
margin: 0px;
|
|
padding: 0px;
|
|
overflow-y: scroll;
|
|
}
|
|
|
|
li {
|
|
user-select: none;
|
|
}
|
|
|
|
summary {
|
|
display: flex;
|
|
}
|
|
|
|
summary.selected {
|
|
color: var(--text-bright);
|
|
background-color: var(--selection-highlight);
|
|
}
|
|
|
|
summary > div, .regulator {
|
|
padding-top: 4px;
|
|
padding-bottom: 4px;
|
|
}
|
|
|
|
.element, .regulator {
|
|
display: flex;
|
|
flex-grow: 1;
|
|
padding-left: 8px;
|
|
padding-right: 8px;
|
|
}
|
|
|
|
.element-switch {
|
|
width: 18px;
|
|
padding-left: 2px;
|
|
text-align: center;
|
|
}
|
|
|
|
details:has(li) .element-switch::after {
|
|
content: '▸';
|
|
}
|
|
|
|
details[open]:has(li) .element-switch::after {
|
|
content: '▾';
|
|
}
|
|
|
|
.element-label {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.regulator-label {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.element-representation {
|
|
display: flex;
|
|
}
|
|
|
|
.element-representation > div {
|
|
padding: 2px 0px 0px 0px;
|
|
font-size: 10pt;
|
|
font-variant-numeric: tabular-nums;
|
|
text-align: right;
|
|
width: 56px;
|
|
}
|
|
|
|
.regulator {
|
|
font-style: italic;
|
|
}
|
|
|
|
.regulator-type {
|
|
padding: 2px 8px 0px 8px;
|
|
font-size: 10pt;
|
|
}
|
|
|
|
.regulator-input {
|
|
color: inherit;
|
|
background-color: inherit;
|
|
border: 1px solid var(--border);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.regulator-input::placeholder {
|
|
color: inherit;
|
|
opacity: 54%;
|
|
font-style: italic;
|
|
}
|
|
|
|
.regulator-input.constraint {
|
|
background-color: var(--display-background);
|
|
}
|
|
|
|
.regulator-input.invalid {
|
|
color: var(--text-invalid);
|
|
border-color: var(--border-invalid);
|
|
}
|
|
|
|
.status {
|
|
width: 20px;
|
|
padding-left: 4px;
|
|
text-align: center;
|
|
font-family: 'Noto Emoji';
|
|
font-style: normal;
|
|
}
|
|
|
|
.regulator-input.invalid + .status::after, details:has(.invalid):not([open]) .status::after {
|
|
content: '⚠';
|
|
color: var(--text-invalid);
|
|
}
|
|
|
|
/* display */
|
|
|
|
canvas {
|
|
float: left;
|
|
margin-left: 20px;
|
|
margin-top: 20px;
|
|
background-color: var(--display-background);
|
|
border: 1px solid var(--border);
|
|
border-radius: 16px;
|
|
}
|
|
|
|
canvas:focus {
|
|
border-color: var(--border-focus-dark);
|
|
outline: none;
|
|
}
|
|
|
|
input:focus {
|
|
border-color: var(--border-focus-light);
|
|
outline: none;
|
|
} |