From 212c9bf9b2eab83ebec591721ba225278cfb197b Mon Sep 17 00:00:00 2001 From: Borys Levytskyi Date: Fri, 12 May 2023 18:44:41 +0300 Subject: [PATCH] Introduce the Settings pane (#49) --- .../components/BitwiseResultView.tsx | 32 ++++++-------- src/expression/module.tsx | 2 +- src/expression/numberParser.test.ts | 10 ++--- src/index.css | 4 ++ src/shell/AppState.ts | 31 ++++++++++---- src/shell/appStateStore.ts | 3 +- src/shell/components/AppRoot.tsx | 16 ++++--- src/shell/components/SettingsPane.css | 24 +++++++++++ src/shell/components/SettingsPane.tsx | 42 +++++++++++++++++++ 9 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 src/shell/components/SettingsPane.css create mode 100644 src/shell/components/SettingsPane.tsx diff --git a/src/expression/components/BitwiseResultView.tsx b/src/expression/components/BitwiseResultView.tsx index 4f44ff6..65fcf5a 100644 --- a/src/expression/components/BitwiseResultView.tsx +++ b/src/expression/components/BitwiseResultView.tsx @@ -13,6 +13,7 @@ import loglevel from 'loglevel'; type BitwiseResultViewProps = { expression: Expression; emphasizeBytes: boolean; + annotateTypes: boolean } type BitwiseResultViewState = { @@ -43,12 +44,7 @@ export default class BitwiseResultView extends React.ComponentError: {text} } - const showInfoColumn : boolean = model.items - .map(i => willInfoColumnBeVisible(i.expressionElement, model!.maxNumberOfBits, allowSignChange)) - .filter(p => p == true) - .length > 0; - - var rows = this.getRows(model!, showInfoColumn, allowSignChange); + var rows = this.getRows(model!, allowSignChange); return @@ -57,7 +53,7 @@ export default class BitwiseResultView extends React.Component } - getRows(model: BitwiseResultViewModel, showInfoColumn : boolean, allowSignChange : boolean): JSX.Element[] { + getRows(model: BitwiseResultViewModel, allowSignChange : boolean): JSX.Element[] { this.maxSeenLengthNumberOfBits = Math.max(model.maxNumberOfBits, this.maxSeenLengthNumberOfBits); @@ -72,7 +68,7 @@ export default class BitwiseResultView extends React.Component this.onValueChanged()} />); } @@ -92,7 +88,7 @@ type ExpressionElementRowProps = { allowSignChange: boolean, expressionItem: ExpressionElement, onValueChanged: any, - showInfoColumn: boolean + showInfoColumn: boolean, } class ExpressionElementTableRow extends React.Component { @@ -221,25 +217,23 @@ getLabel(): string { const children = []; let title = `BitwiseCmd treats this number as ${op.value.maxBitSize}-bit integer`; let text = `${op.value.maxBitSize}-bit `; + const signedStr = op.value.signed ? 'signed' : 'unsigned'; - const signedOther = op.value.signed ? 'usigned' : 'signed'; - const signedTitle = `Click to change to ${signedOther} preserving the same bits`; + const signedOther = op.value.signed ? 'unsigned' : 'signed'; + const signedButtonTitle = `Click to change to ${signedOther} preserving the same bits`; if(op.label.length > 0) { text += " (converted)"; - title += ". This number was converted to facilitate bitwise operation with an operand of a different type"; + title += ". This number was converted to facilitate bitwise operation with an operand of a different type."; } children.push({text.trim()}); - if(this.props.maxNumberOfBits >= op.value.maxBitSize) - { - if(allowSignChange) - children.push(); - else if(!op.value.signed) - children.push( {signedStr}) - } + if(allowSignChange) + children.push(); + else + children.push( {signedStr}) return {children} } diff --git a/src/expression/module.tsx b/src/expression/module.tsx index e187ce4..0fd1ca2 100644 --- a/src/expression/module.tsx +++ b/src/expression/module.tsx @@ -12,7 +12,7 @@ const expressionAppModule = { canHandle: (input:string) => parser.canParse(input), handle: function(c: CommandInput) { var expr = parser.parse(c.input); - appState.addCommandResult(c.input, () => ); + appState.addCommandResult(c.input, () => ); } }); } diff --git a/src/expression/numberParser.test.ts b/src/expression/numberParser.test.ts index 623750e..82bf62e 100644 --- a/src/expression/numberParser.test.ts +++ b/src/expression/numberParser.test.ts @@ -105,7 +105,7 @@ describe("parser", () => { expect(v?.num()).toBe(1); }); - it('fits usigned int32 max value into 32-bit data type', () => { + it('fits unsigned int32 max value into 32-bit data type', () => { const n1 = numberParser.parse("4294967295u"); const n2 = numberParser.parse("4294967296u"); @@ -125,28 +125,28 @@ describe("parser", () => { //expect(v2).toEqual(v); }); - it('parses usigned single', () => { + it('parses unsigned single', () => { var v = numberParser.parse('1us')?.value expect(v?.maxBitSize).toBe(16); expect(v?.num()).toBe(1); expect(v?.signed).toBe(false); }); - it('parses usigned int32', () => { + it('parses unsigned int32', () => { var v = numberParser.parse('1u')?.value expect(v?.maxBitSize).toBe(32); expect(v?.num()).toBe(1); expect(v?.signed).toBe(false); }); - it('parses usigned byte', () => { + it('parses unsigned byte', () => { var v = numberParser.parse('1ub')?.value expect(v?.maxBitSize).toBe(8); expect(v?.num()).toBe(1); expect(v?.signed).toBe(false); }); - it('parses usigned long', () => { + it('parses unsigned long', () => { var v = numberParser.parse('1ul')?.value expect(v?.maxBitSize).toBe(64); expect(v?.num()).toBe(1); diff --git a/src/index.css b/src/index.css index d0c1cb5..2708944 100644 --- a/src/index.css +++ b/src/index.css @@ -75,6 +75,10 @@ a.hashLink { font-size: 1.1em;} button { border: none; text-decoration: underline;} +.settings-button { + margin-left: -20px; +} + .undo button { opacity: 0.4; padding: 0; diff --git a/src/shell/AppState.ts b/src/shell/AppState.ts index bcf7a82..1a0e3f9 100644 --- a/src/shell/AppState.ts +++ b/src/shell/AppState.ts @@ -8,7 +8,8 @@ export type PersistedAppData = { version: number | null; debugMode: boolean | null; pageVisistsCount: number; - donationClicked: boolean + donationClicked: boolean; + annotateTypes: boolean } export type CommandResultView = { @@ -26,27 +27,28 @@ export default class AppState { version: number = APP_VERSION; emphasizeBytes: boolean; debugMode: boolean = false; - uiTheme: string; - changeHandlers: AppStateChangeHandler[]; - commandResults: CommandResultView[]; + uiTheme: string = 'midnight'; + changeHandlers: AppStateChangeHandler[] = []; + commandResults: CommandResultView[] = []; persistedVersion: number; wasOldVersion: boolean; env: string; pageVisitsCount: number; donationClicked: boolean; + showSettings: boolean = false; + annotateTypes: boolean = false; constructor(persistData : PersistedAppData, env: string) { - this.commandResults = []; - this.changeHandlers = []; - this.uiTheme = persistData.uiTheme || 'midnight'; + this.env = env; - this.emphasizeBytes = !!persistData.emphasizeBytes; + this.emphasizeBytes = !!persistData.emphasizeBytes; this.persistedVersion = persistData.version || 0.1; this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion; this.debugMode = persistData.debugMode === true; this.pageVisitsCount = persistData.pageVisistsCount || 0; this.donationClicked = persistData.donationClicked; + this.annotateTypes = !!persistData.annotateTypes; } addCommandResult(input : string, view : ViewFactory) { @@ -92,6 +94,16 @@ export default class AppState { this.triggerChanged(); } + toggleShowSettings() { + this.showSettings = !this.showSettings; + this.triggerChanged(); + } + + toggleAnnotateTypes() { + this.annotateTypes = !this.annotateTypes; + this.triggerChanged(); + } + registerVisit() { this.pageVisitsCount++; this.triggerChanged(); @@ -112,7 +124,8 @@ export default class AppState { version: this.version, debugMode: this.debugMode, pageVisistsCount: this.pageVisitsCount, - donationClicked: this.donationClicked + donationClicked: this.donationClicked, + annotateTypes: this.annotateTypes } } }; diff --git a/src/shell/appStateStore.ts b/src/shell/appStateStore.ts index 0bb0d6b..776e87a 100644 --- a/src/shell/appStateStore.ts +++ b/src/shell/appStateStore.ts @@ -8,7 +8,8 @@ const DEFAULT_DATA : PersistedAppData = { version: APP_VERSION, debugMode: false, pageVisistsCount: 0, - donationClicked: false + donationClicked: false, + annotateTypes: false } export default { diff --git a/src/shell/components/AppRoot.tsx b/src/shell/components/AppRoot.tsx index 692e8c0..8b87896 100644 --- a/src/shell/components/AppRoot.tsx +++ b/src/shell/components/AppRoot.tsx @@ -8,6 +8,9 @@ import DebugIndicators from './DebugIndicators'; import hash from '../../core/hash'; import TopLinks from './TopLinks'; import Toggle from './Toggle'; +import SettingsPane from './SettingsPane'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faGear } from '@fortawesome/free-solid-svg-icons'; type AppRootProps = { @@ -44,15 +47,11 @@ export default class AppRoot extends React.Component return results; } - toggleEmphasizeBytes() { - this.props.appState.toggleEmphasizeBytes(); - } - render() { const enableNewUi = this.props.appState.env != 'prod' || true; const newUi = enableNewUi ? 'new-ui' : ''; - + const settingsCss = "settings-button" + (this.props.appState.showSettings ? '' : ' soft'); return
@@ -63,12 +62,11 @@ export default class AppRoot extends React.Component
cmd.execute(input)} /> + + - - this.toggleEmphasizeBytes()} title="Toggle Emphasize Bytes" /> -
- + {this.props.appState.showSettings ? : null}
{this.getResultViews()}
diff --git a/src/shell/components/SettingsPane.css b/src/shell/components/SettingsPane.css new file mode 100644 index 0000000..ab9a97b --- /dev/null +++ b/src/shell/components/SettingsPane.css @@ -0,0 +1,24 @@ +.settings-container { + padding: 20px; + display: inline-block; +} + +.settings-container button { + text-decoration: none; +} + +.settings-container button svg { + margin-right: 3px; +} + +.settings-container .description { + font-size: 0.85em; + padding: 0; + margin: 0; + padding-left: 30px; + opacity: 0.8; +} + +.settings-container .setting { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/src/shell/components/SettingsPane.tsx b/src/shell/components/SettingsPane.tsx new file mode 100644 index 0000000..13bc65e --- /dev/null +++ b/src/shell/components/SettingsPane.tsx @@ -0,0 +1,42 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import './SettingsPane.css'; +import { faToggleOff, faToggleOn } from '@fortawesome/free-solid-svg-icons'; +import AppState from '../AppState'; + +type SettingsPaneProps = { + appState : AppState +} + +function SettingsPane(props : SettingsPaneProps) { + + const {appState} = props; + + return
+
+

Settings

+
+ +

+ {appState.emphasizeBytes + ? "Each binary string is extended to contain at least 8 bits. A white space is added between each group of 8 bits so it easy to tell bytes apart." + : "Binary strings are not modified."} +

+
+
+ +

+ {appState.annotateTypes + ? "BitwiseCmd shows the integer size and indicates whether the data type is signed or unsigned." + : "Information about the size of integers used in the calculation is hidden."} +

+
+ +
+
+} + +export default SettingsPane; \ No newline at end of file