diff --git a/media/editor/dataDisplayContext.tsx b/media/editor/dataDisplayContext.tsx index 9ea7488..9b037a2 100644 --- a/media/editor/dataDisplayContext.tsx +++ b/media/editor/dataDisplayContext.tsx @@ -244,6 +244,13 @@ export class DisplayContext { }); this.selectionChangeEmitter.addListener(() => this.publishSelections()); + this.hoverChangeEmitter.addListener(() => { + // publishes the new hovered byte + messageHandler.sendEvent({ + type: MessageType.SetHoveredByte, + hovered: this._hoveredByte?.byte, + }) + }); } /** diff --git a/shared/protocol.ts b/shared/protocol.ts index d1320c2..a48a0a0 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -14,6 +14,7 @@ export const enum MessageType { ReloadFromDisk, StashDisplayedOffset, GoToOffset, + SetHoveredByte, SetFocusedByte, SetFocusedByteRange, SetSelectedCount, @@ -145,6 +146,12 @@ export interface SetSelectedCountMessage { focused?: number; } +/** Sets the hovered byte in the editor */ +export interface SetHoveredByteMessage { + type: MessageType.SetHoveredByte; + hovered?: number; +} + /** Saves the current offset shown in the editor. */ export interface StashDisplayedOffsetMessage { type: MessageType.StashDisplayedOffset; @@ -254,6 +261,7 @@ export type FromWebviewMessage = | ClearDataInspectorMessage | SetInspectByteMessage | SetSelectedCountMessage + | SetHoveredByteMessage | ReadyRequestMessage | UpdateEditorSettings | PasteMessage diff --git a/src/extension.ts b/src/extension.ts index 563e7c6..208632c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,7 +8,8 @@ import { showGoToOffset } from "./goToOffset"; import { HexEditorProvider } from "./hexEditorProvider"; import { HexEditorRegistry } from "./hexEditorRegistry"; import { showSelectBetweenOffsets } from "./selectBetweenOffsets"; -import StatusSelectionCount from "./statusSelectionCount"; +import StatusFocus from "./statusFocus"; +import StatusHoverAndSelection from "./statusHoverAndSelection"; function readConfigFromPackageJson(extension: vscode.Extension): { extId: string; @@ -69,7 +70,8 @@ export function activate(context: vscode.ExtensionContext): void { } }, ); - context.subscriptions.push(new StatusSelectionCount(registry)); + context.subscriptions.push(new StatusFocus(registry)); + context.subscriptions.push(new StatusHoverAndSelection(registry)); context.subscriptions.push(goToOffsetCommand); context.subscriptions.push(selectBetweenOffsetsCommand); context.subscriptions.push(openWithCommand); diff --git a/src/hexDocument.ts b/src/hexDocument.ts index be18f68..5b16842 100644 --- a/src/hexDocument.ts +++ b/src/hexDocument.ts @@ -62,7 +62,7 @@ export class HexDocument extends Disposable implements vscode.CustomDocument { public lastSave = 0; private _selectionState: ISelectionState = { selected: 0 }; - + private _hoverState: number | undefined = undefined; /** Search provider for the document. */ public readonly searchProvider = new SearchProvider(); @@ -161,6 +161,21 @@ export class HexDocument extends Disposable implements vscode.CustomDocument { this._onDidChangeSelectionState.fire(state); } + private readonly _onDidChangeHoverState = this._register( + new vscode.EventEmitter(), + ); + + public readonly onDidChangeHoverState = this._onDidChangeHoverState.event; + + public get hoverState() { + return this._hoverState; + } + + public set hoverState(byte: number | undefined) { + this._hoverState = byte; + this._onDidChangeHoverState.fire(byte); + } + private readonly _onDidRevert = this._register(new vscode.EventEmitter()); /** diff --git a/src/hexEditorProvider.ts b/src/hexEditorProvider.ts index 13ad13b..77fce1c 100644 --- a/src/hexEditorProvider.ts +++ b/src/hexEditorProvider.ts @@ -328,7 +328,9 @@ export class HexEditorProvider implements vscode.CustomEditorProvider 1 ? numberFormat.format(selected) : undefined; - if (nFocus && nSelected) { - this.item.text = vscode.l10n.t("Byte {0}/0x{1} ({2}/0x{3} selected)", nFocus, focused!.toString(16).toUpperCase(), nSelected, selected.toString(16).toUpperCase()); - } else if (nSelected) { - this.item.text = vscode.l10n.t("{0}/0x{1} selected", nSelected, selected.toString(16).toUpperCase()); - } else if (nFocus) { - this.item.text = vscode.l10n.t("Byte {0}/0x{1}", nFocus, focused!.toString(16).toUpperCase()); + if (nFocus) { + this.item.text = vscode.l10n.t("{0}/0x{1}", nFocus, focused!.toString(16).toUpperCase()); + this.item.show(); } else { this.item.hide(); return; } - - this.item.show(); } show() { diff --git a/src/statusHoverAndSelection.ts b/src/statusHoverAndSelection.ts new file mode 100644 index 0000000..dffe74a --- /dev/null +++ b/src/statusHoverAndSelection.ts @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as vscode from "vscode"; +import { Disposable, DisposableValue } from "./dispose"; +import { HexDocument } from "./hexDocument"; +import { HexEditorRegistry } from "./hexEditorRegistry"; + +const numberFormat = new Intl.NumberFormat(); + +/** + * Displays the hovered byte and the selection count in a StatusBarItem + * + * @class StatusHoverAndSelection + */ +export default class StatusHoverAndSelection extends Disposable { + private readonly item: vscode.StatusBarItem; + private readonly docChangeListener = this._register(new DisposableValue()); + + constructor(registry: HexEditorRegistry) { + super(); + + this.item = this._register( + // Primary Badge, so appears first + vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 101), + ); + + const trackDocument = (doc: HexDocument | undefined) => { + if (doc) { + this._register(doc.onDidChangeHoverState(() => this.update(doc))); + this._register(doc.onDidChangeSelectionState(() => this.update(doc))); + this.update(doc); + this.show(); + } else { + this.hide(); + } + }; + + this._register(registry.onDidChangeActiveDocument(trackDocument)); + trackDocument(registry.activeDocument); + } + + update({ hoverState, selectionState }: HexDocument): void { + const { selected } = selectionState; + const nHovered = hoverState !== undefined ? numberFormat.format(hoverState) : undefined; + const nSelected = selected > 1 ? numberFormat.format(selected) : undefined; + if (nHovered && nSelected) { + this.item.text = vscode.l10n.t( + "{0}/0x{1} ({2}/0x{3} selected)", + nHovered, + hoverState!.toString(16).toUpperCase(), + nSelected, + selected!.toString(16).toUpperCase(), + ); + } else if (nHovered) { + this.item.text = vscode.l10n.t("{0}/0x{1}", nHovered, hoverState!.toString(16).toUpperCase()); + } else if (nSelected) { + this.item.text = vscode.l10n.t( + "{0}/0x{1} selected", + nSelected, + selected!.toString(16).toUpperCase(), + ); + } else { + // Hiding the element creates a flashing effect so instead set it + // to an empty string + this.item.text = vscode.l10n.t(""); + } + + this.item.show(); + } + + show() { + this.item.show(); + } + + hide() { + this.item.hide(); + } +}