diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx index ff70947ff..6bbc3c4c1 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx @@ -21,7 +21,15 @@ import { AsyncLocalizationProvider, LanguageInfo, } from '@theia/core/lib/common/i18n/localization'; +import SettingsStepInput from './settings-step-input'; +const maxScale = 200; +const minScale = -100; +const scaleStep = 20; + +const maxFontSize = 72; +const minFontSize = 0; +const fontSizeStep = 2; export class SettingsComponent extends React.Component< SettingsComponent.Props, SettingsComponent.State @@ -88,6 +96,8 @@ export class SettingsComponent extends React.Component< } protected renderSettings(): React.ReactNode { + const scalePercentage = 100 + this.state.interfaceScale * 20; + return (
{nls.localize( @@ -160,14 +170,13 @@ export class SettingsComponent extends React.Component<
-
@@ -179,14 +188,13 @@ export class SettingsComponent extends React.Component< /> {nls.localize('arduino/preferences/automatic', 'Automatic')} - %
@@ -516,13 +524,8 @@ export class SettingsComponent extends React.Component< } }; - protected editorFontSizeDidChange = ( - event: React.ChangeEvent - ): void => { - const { value } = event.target; - if (value) { - this.setState({ editorFontSize: parseInt(value, 10) }); - } + private setFontSize = (editorFontSize: number) => { + this.setState({ editorFontSize }); }; protected rawAdditionalUrlsValueDidChange = ( @@ -539,18 +542,10 @@ export class SettingsComponent extends React.Component< this.setState({ autoScaleInterface: event.target.checked }); }; - protected interfaceScaleDidChange = ( - event: React.ChangeEvent - ): void => { - const { value } = event.target; - const percentage = parseInt(value, 10); - if (isNaN(percentage)) { - return; - } + private setInterfaceScale = (percentage: number) => { const interfaceScale = (percentage - 100) / 20; - if (!isNaN(interfaceScale)) { - this.setState({ interfaceScale }); - } + + this.setState({ interfaceScale }); }; protected verifyAfterUploadDidChange = ( diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx new file mode 100644 index 000000000..458266fa9 --- /dev/null +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx @@ -0,0 +1,147 @@ +import * as React from '@theia/core/shared/react'; +import classnames from 'classnames'; + +interface SettingsStepInputProps { + value: number; + setSettingsStateValue: (value: number) => void; + step: number; + maxValue: number; + minValue: number; + classNames?: { [key: string]: string }; +} + +const SettingsStepInput: React.FC = ( + props: SettingsStepInputProps +) => { + const { value, setSettingsStateValue, step, maxValue, minValue, classNames } = + props; + + const [stepUpDisabled, setStepUpDisabled] = React.useState(false); + const [stepDownDisabled, setStepDownDisabled] = React.useState(false); + + const onStepUp = (): void => { + const valueRoundedToScale = Math.ceil(value / step) * step; + const calculatedValue = + valueRoundedToScale === value ? value + step : valueRoundedToScale; + const newValue = limitValueByCondition( + calculatedValue, + maxValue, + calculatedValue >= maxValue, + disableStepUp + ); + + setSettingsStateValue(newValue); + }; + + const onStepDown = (): void => { + const valueRoundedToScale = Math.floor(value / step) * step; + const calculatedValue = + valueRoundedToScale === value ? value - step : valueRoundedToScale; + const newValue = limitValueByCondition( + calculatedValue, + minValue, + calculatedValue <= minValue, + disableStepDown + ); + + setSettingsStateValue(newValue); + }; + + const limitValueByCondition = ( + calculatedValue: number, + limitedValue: number, + condition: boolean, + onConditionTrue: () => void, + onConditionFalse = enableButtons + ): number => { + if (condition) { + onConditionTrue(); + return limitedValue; + } else { + onConditionFalse(); + return calculatedValue; + } + }; + + const enableButtons = (): void => { + setStepUpDisabled(false); + setStepDownDisabled(false); + }; + + const disableStepUp = (): void => { + setStepUpDisabled(true); + }; + + const disableStepDown = (): void => { + setStepDownDisabled(true); + }; + + const onUserInput = (event: React.ChangeEvent): void => { + const { value: eventValue } = event.target; + + if (eventValue === '') { + setSettingsStateValue(0); + } + + const number = Number(eventValue); + + if (!isNaN(number) && number !== value) { + let newValue; + if (number > value) { + newValue = limitValueByCondition( + number, + maxValue, + number >= maxValue, + disableStepUp + ); + } else { + newValue = limitValueByCondition( + number, + minValue, + number <= minValue, + disableStepDown + ); + } + + setSettingsStateValue(newValue); + } + }; + + // the component does not unmount when we close the settings dialog + // in theia which necessitates the below useEffect + React.useEffect(() => { + if (value > minValue && value < maxValue) { + enableButtons(); + } + }, [value, minValue, maxValue]); + + return ( +
+ +
+ + +
+
+ ); +}; + +export default SettingsStepInput; diff --git a/arduino-ide-extension/src/browser/style/index.css b/arduino-ide-extension/src/browser/style/index.css index fe5d9753f..1a7c98533 100644 --- a/arduino-ide-extension/src/browser/style/index.css +++ b/arduino-ide-extension/src/browser/style/index.css @@ -18,6 +18,7 @@ @import './fonts.css'; @import './custom-codicon.css'; @import './progress-bar.css'; +@import './settings-step-input.css'; .theia-input.warning:focus { outline-width: 1px; diff --git a/arduino-ide-extension/src/browser/style/settings-step-input.css b/arduino-ide-extension/src/browser/style/settings-step-input.css new file mode 100644 index 000000000..fd6d6046f --- /dev/null +++ b/arduino-ide-extension/src/browser/style/settings-step-input.css @@ -0,0 +1,47 @@ +.settings-step-input-container { + position: relative +} + +.settings-step-input-element::-webkit-inner-spin-button, +.settings-step-input-element::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.settings-step-input-buttons-container { + display: none; + flex-direction: column; + position: absolute; + right: 0px; + top: 50%; + transform: translate(0px, -50%); + height: calc(100% - 4px); + width: 14px; + padding: 2px; + background: white; +} + +.settings-step-input-container:hover > .settings-step-input-buttons-container { + display: flex; +} + +.settings-step-input-up-button { + transform: rotate(-180deg); +} + +.settings-step-input-button { + border: none; + border-radius: 0; + height: 50%; + width: 1; + display: flex; + align-items: center; + justify-content: center; + user-select: none; + cursor: pointer; + line-height: 12px; +} + +.settings-step-input-button:hover { + background: rgba(128, 128, 128, 0.8); +}