This is an automated email from the ASF dual-hosted git repository.

rstrickland pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git


The following commit(s) were added to refs/heads/main by this push:
     new 1475de6  Implemented Configurable Data Viewport Geometry
1475de6 is described below

commit 1475de6bc323f14b68859d9e53e25ed33d872662
Author: Robert Strickland <[email protected]>
AuthorDate: Fri Oct 6 11:24:50 2023 -0500

    Implemented Configurable Data Viewport Geometry
    
    - Added the ability to hide/show viewports.
    - Added the ability to change the amount of bytes per row shown and be
      independent of the current display radix.
    - Added the ability to increment/decrement the amount of viewport lines
      being displayed.
    - Reworked CSS for viewport data tables to move from `display:grid;` to
      `display:flex;`.
    
    Closes #864
    Closes #797
    Closes #794
---
 .../DataDisplays/CustomByteDisplay/BinaryData.ts   |   8 +-
 .../CustomByteDisplay/DataLineFeed.svelte          |  89 +++++++--
 .../DataDisplays/Header/DisplayHeader.svelte       | 173 ++++++++---------
 .../src/components/Editors/DataEditor.svelte       | 114 +++++++++--
 .../components/Header/fieldsets/Settings.svelte    |   4 +-
 .../components/Icons/ViewportVisibilityIcon.svelte | 214 +++++++++++++++++++++
 .../src/components/Inputs/Buttons/Button.svelte    |   4 +-
 src/svelte/src/components/Main.svelte              |  40 ++--
 src/svelte/src/components/dataEditor.svelte        |  21 +-
 src/svelte/src/components/globalStyles.css         |  28 ++-
 src/svelte/src/stores/configuration.ts             |  12 +-
 src/svelte/src/stores/index.ts                     |   8 +-
 12 files changed, 541 insertions(+), 174 deletions(-)

diff --git 
a/src/svelte/src/components/DataDisplays/CustomByteDisplay/BinaryData.ts 
b/src/svelte/src/components/DataDisplays/CustomByteDisplay/BinaryData.ts
index 95e75e4..6da7477 100644
--- a/src/svelte/src/components/DataDisplays/CustomByteDisplay/BinaryData.ts
+++ b/src/svelte/src/components/DataDisplays/CustomByteDisplay/BinaryData.ts
@@ -17,15 +17,15 @@
 
 import { SimpleWritable } from '../../../stores/localStore'
 import {
-  NUM_LINES_DISPLAYED,
   type BytesPerRow,
   type RadixValues,
-  VIEWPORT_CAPACITY_MAX,
 } from '../../../stores/configuration'
 import {
   radixBytePad,
   viewport_offset_to_line_num,
 } from '../../../utilities/display'
+import { dataDislayLineAmount } from '../../../stores'
+import { get } from 'svelte/store'
 
 export const BYTE_ACTION_DIV_OFFSET: number = 24
 
@@ -112,7 +112,7 @@ export class ViewportDataStore_t extends 
SimpleWritable<ViewportData_t> {
   public upperFetchBoundary(bytesPerRow: BytesPerRow): number {
     const store = this.storeData()
     const boundary =
-      store.fileOffset + store.length - NUM_LINES_DISPLAYED * bytesPerRow
+      store.fileOffset + store.length - get(dataDislayLineAmount) * bytesPerRow
 
     return boundary
   }
@@ -120,7 +120,7 @@ export class ViewportDataStore_t extends 
SimpleWritable<ViewportData_t> {
   public lineTopMax(bytesPerRow: BytesPerRow): number {
     const vpMaxOffset = Math.max(
       0,
-      this.storeData().length - NUM_LINES_DISPLAYED * bytesPerRow
+      this.storeData().length - get(dataDislayLineAmount) * bytesPerRow
     )
     const vpLineTopMax = viewport_offset_to_line_num(
       vpMaxOffset + this.storeData().fileOffset,
diff --git 
a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte 
b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte
index 5400cc9..644ec8e 100644
--- 
a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte
+++ 
b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte
@@ -29,10 +29,11 @@ limitations under the License.
     editorActionsAllowed,
     dataFeedLineTop,
     seekOffsetInput,
+    visableViewports,
+    dataDislayLineAmount,
   } from '../../../stores'
   import {
     EditByteModes,
-    NUM_LINES_DISPLAYED,
     type RadixValues,
     EditActionRestrictions,
     VIEWPORT_SCROLL_INCREMENT,
@@ -90,7 +91,7 @@ limitations under the License.
       if (fetchBound > $fileMetrics.computedSize)
         return (
           ($fileMetrics.computedSize / $bytesPerRow) * $bytesPerRow -
-          NUM_LINES_DISPLAYED * $bytesPerRow
+          $dataDislayLineAmount * $bytesPerRow
         )
 
       return fetchBound
@@ -124,7 +125,7 @@ limitations under the License.
   }
   const INCREMENT_SEGMENT = () => {
     $seekOffsetInput = line_num_to_file_offset(
-      $dataFeedLineTop + NUM_LINES_DISPLAYED,
+      $dataFeedLineTop + $dataDislayLineAmount,
       viewportData.fileOffset,
       $bytesPerRow
     ).toString(addressRadix)
@@ -132,7 +133,7 @@ limitations under the License.
   }
   const DECREMENT_SEGMENT = () => {
     $seekOffsetInput = line_num_to_file_offset(
-      $dataFeedLineTop - NUM_LINES_DISPLAYED,
+      $dataFeedLineTop - $dataDislayLineAmount,
       viewportData.fileOffset,
       $bytesPerRow
     ).toString(addressRadix)
@@ -175,7 +176,7 @@ limitations under the License.
     INCREMENT = 1,
   }
 
-  let height = `calc(${NUM_LINES_DISPLAYED} * 20)px`
+  let height = `calc(${$dataDislayLineAmount} * 20)px`
   let viewportLines: Array<ViewportLineData> = []
   let viewportDataContainer: HTMLDivElement
   let selectedByteElement: HTMLDivElement
@@ -195,9 +196,9 @@ limitations under the License.
   $: {
     totalLinesPerFilesize = Math.ceil($fileMetrics.computedSize / $bytesPerRow)
     totalLinesPerViewport = Math.ceil(viewportData.data.length / $bytesPerRow)
-    lineTopMaxFile = Math.max(totalLinesPerFilesize - NUM_LINES_DISPLAYED, 0)
+    lineTopMaxFile = Math.max(totalLinesPerFilesize - $dataDislayLineAmount, 0)
     lineTopMaxViewport = Math.max(
-      totalLinesPerViewport - NUM_LINES_DISPLAYED,
+      totalLinesPerViewport - $dataDislayLineAmount,
       0
     )
 
@@ -247,9 +248,9 @@ limitations under the License.
     startIndex: number,
     dataRadix: RadixValues,
     addressRadix: RadixValues,
-    endIndex: number = startIndex + (NUM_LINES_DISPLAYED - 1)
+    endIndex: number = startIndex + ($dataDislayLineAmount - 1)
   ): Array<ViewportLineData> {
-    let ret = []
+    let ret: Array<ViewportLineData> = []
     for (let i = startIndex; i <= endIndex; i++) {
       const viewportLineOffset = i * $bytesPerRow
       const fileOffset = viewportLineOffset + viewportData.fileOffset
@@ -438,7 +439,7 @@ limitations under the License.
         ($fileMetrics.computedSize * (percentageTraversed / 100.0)) /
           $bytesPerRow
       ) * $bytesPerRow
-    const firstPageThreshold = $bytesPerRow * NUM_LINES_DISPLAYED
+    const firstPageThreshold = $bytesPerRow * $dataDislayLineAmount
     const lastPageThreshold = $fileMetrics.computedSize - firstPageThreshold
     if (offset <= firstPageThreshold) {
       // scroll to the top because we are somewhere in the first page
@@ -513,6 +514,8 @@ limitations under the License.
       <div class="address" id="address">
         <b>{viewportLine.offset}</b>
       </div>
+
+      {#if $visableViewports === 'physical' || $visableViewports === 'all'}
       <div
         class="byte-line"
         id="physical-line-{i.toString(16).padStart(2, '0')}"
@@ -534,6 +537,8 @@ limitations under the License.
           />
         {/each}
       </div>
+      {/if}
+      {#if $visableViewports === 'logical' || $visableViewports === 'all'}
       <div
         class="byte-line"
         id="logical-line-{i.toString(16).padStart(2, '0')}"
@@ -555,6 +560,7 @@ limitations under the License.
           />
         {/each}
       </div>
+      {/if}
     </div>
   {/each}
 
@@ -564,7 +570,7 @@ limitations under the License.
       selectionActive={$selectionDataStore.active}
       currentLine={$dataFeedLineTop}
       fileOffset={viewportData.fileOffset}
-      maxDisplayLines={NUM_LINES_DISPLAYED}
+      maxDisplayLines={$dataDislayLineAmount}
       bind:percentageTraversed
       on:indicatorClicked={handleClickedIndicator}
     />
@@ -584,7 +590,7 @@ limitations under the License.
         fn={INCREMENT_SEGMENT}
         disabledBy={disableIncrement}
         width="30pt"
-        description="Increment offset by {NUM_LINES_DISPLAYED *
+        description="Increment offset by {$dataDislayLineAmount *
           $bytesPerRow} bytes"
         tooltipAlwaysEnabled={true}
       >
@@ -618,7 +624,7 @@ limitations under the License.
         fn={DECREMENT_SEGMENT}
         disabledBy={disableDecrement}
         width="30pt"
-        description="Decrement offset by {NUM_LINES_DISPLAYED *
+        description="Decrement offset by {$dataDislayLineAmount *
           $bytesPerRow} bytes"
         tooltipAlwaysEnabled={true}
       >
@@ -637,6 +643,45 @@ limitations under the License.
           >stat_3</span
         >
       </Button>
+      <span class="separator" />
+      <Button
+        fn={() => { 
+            $dataDislayLineAmount += 1
+            viewportLines = generate_line_data(
+              $dataFeedLineTop,
+              dataRadix,
+              addressRadix
+              )
+          }
+        }
+        disabledBy={ (($dataDislayLineAmount+1)*$bytesPerRow >= 
VIEWPORT_SCROLL_INCREMENT) }
+        description={"Increment display lines (" + ($dataDislayLineAmount+1) + 
")"}
+        tooltipAlwaysEnabled={true}
+        width="30pt"
+      >
+        <span slot="left" class="btn-icon material-symbols-outlined">
+          playlist_add
+        </span>
+      </Button>
+      <Button
+        fn={() => { 
+            $dataDislayLineAmount -= 1
+            viewportLines = generate_line_data(
+              $dataFeedLineTop,
+              dataRadix,
+              addressRadix
+              )
+          }
+        }
+        disabledBy={$dataDislayLineAmount === 1}
+        description={"Decrement display lines (" + ($dataDislayLineAmount-1) + 
")"}
+        tooltipAlwaysEnabled={true}
+        width="30pt"
+      >
+        <span slot="left" class="btn-icon material-symbols-outlined">
+          playlist_remove
+        </span>
+      </Button>
     </FlexContainer>
   </FlexContainer>
 </div>
@@ -645,6 +690,22 @@ limitations under the License.
   span {
     font-weight: bold;
   }
+  span.separator {
+    border: 1px solid grey;
+    opacity: 0.5;
+    display: flex;
+    flex-direction: column;
+    margin: 0 5px;
+  }
+  span.submit-bpr-input {
+    font-size: 14px;
+    cursor: pointer;
+    margin: 0 5px;
+  }
+  span.submit-bpr-input:hover {
+    font-weight: bold;
+    cursor: pointer;
+  }
   div.container {
     display: flex;
     flex-direction: column;
@@ -659,7 +720,7 @@ limitations under the License.
   div.container div.line {
     display: flex;
     flex-direction: row;
-    width: 100%;
+    width: fit-content;
     height: 24px;
   }
   div.container div.line div {
diff --git a/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte 
b/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte
index ce26a4b..e51495f 100644
--- a/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte
+++ b/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte
@@ -18,7 +18,6 @@ limitations under the License.
   import {
     addressRadix,
     displayRadix,
-    editMode,
     seekOffset,
     seekOffsetInput,
     selectionDataStore,
@@ -26,9 +25,9 @@ limitations under the License.
     selectionSize,
     bytesPerRow,
     viewport,
+    visableViewports
   } from '../../../stores'
   import {
-    EditByteModes,
     ADDRESS_RADIX_OPTIONS,
     type RadixValues,
     type BytesPerRow,
@@ -37,21 +36,12 @@ limitations under the License.
   import { UIThemeCSSClass } from '../../../utilities/colorScheme'
   import { createEventDispatcher } from 'svelte'
   import { OffsetSearchType } from '../../Header/fieldsets/SearchReplace'
-
-  type ViewportDivSpread = '24px' | '28px' | '68px'
+  import { byteDivWidthFromRadix } from '../../../utilities/display'
 
   const eventDispatcher = createEventDispatcher()
-  const bitNumText = '01234567'
-  const physicalOffsetSpreads = {
-    16: '24px',
-    10: '28px',
-    8: '28px',
-    2: '68px',
-  }
-
-  let phyiscalOffsetSpread: ViewportDivSpread
+  let bitIndexStr = '01234567'
   let selectionOffsetText: string
-  let offsetLine = []
+  let offsetLine: string[] = []
 
   $: {
     offsetLine = generate_offset_headers(
@@ -74,20 +64,17 @@ limitations under the License.
     displayRadix: RadixValues,
     bytesPerRow: BytesPerRow
   ) {
-    let ret = []
+    let ret: string[] = []
 
     if (displayRadix != RADIX_OPTIONS.Binary) {
       for (let i = 0; i < bytesPerRow; i++) {
         ret.push(i.toString(addressRadix).padStart(2, '0'))
       }
     } else {
-      for (let i = 0; i < 8; i++) {
-        ret.push(i.toString(10))
+      for (let i = 0; i < bytesPerRow; i++) {
+        ret.push(i.toString(addressRadix))
       }
     }
-    phyiscalOffsetSpread = physicalOffsetSpreads[
-      displayRadix
-    ] as ViewportDivSpread
     return ret
   }
 
@@ -120,92 +107,102 @@ limitations under the License.
     }
     $addressRadix = newAddrRadix
   }
-
-  function clearDataDisplays() {
-    eventDispatcher('clearDataDisplays')
-  }
 </script>
 
-<div class={$UIThemeCSSClass + ' hd'}>Address</div>
-<div class={$UIThemeCSSClass + ' hd'}>Physical</div>
-<div class={$UIThemeCSSClass + ' hd'}>Logical</div>
-<div class={$UIThemeCSSClass + ' hd'}>Edit</div>
-<div class={$UIThemeCSSClass + ' measure'} style="align-items: center;">
-  <select
-    class={$UIThemeCSSClass + ' address_type'}
-    id="address_numbering"
-    on:change={updateAddressValue}
-  >
-    {#each ADDRESS_RADIX_OPTIONS as { name, value }}
-      <option {value}>{name}</option>
-    {/each}
-  </select>
-</div>
-
-<div class={$UIThemeCSSClass + ' measure physical-viewport-header'}>
-  {#if $displayRadix === RADIX_OPTIONS.Binary}
-    {#each offsetLine as offset}
-      <div class="phyiscal-addr-seg binary" style:width={phyiscalOffsetSpread}>
-        <div>{offset}</div>
-        <div>{bitNumText}</div>
-      </div>
-    {/each}
-  {:else}
-    {#each offsetLine as offset}
-      <div class="physical-addr-seg" style:width={phyiscalOffsetSpread}>
-        {offset}
-      </div>
-    {/each}
-  {/if}
-</div>
-
-<div class={$UIThemeCSSClass + ' measure logical-viewport-header'}>
-  {#each offsetLine as offset}
-    <div class="logical-addr-seg">{offset}</div>
-  {/each}
-</div>
-
-<div class={$UIThemeCSSClass + ' measure selection'}>
-  {#if $selectionDataStore.active && $editMode !== EditByteModes.Single}
-    <!-- svelte-ignore a11y-no-static-element-interactions -->
-    <div
-      class="clear-selection"
-      title="Clear selection data"
-      on:click={clearDataDisplays}
-      on:keypress={clearDataDisplays}
+<div class="headers">
+  <div class="hdr address-header" style:min-width=110px>
+    <div class={$UIThemeCSSClass + ' hd'}>Address</div>
+    <select
+      class={$UIThemeCSSClass + ' address_type'}
+      id="address_numbering"
+      on:change={updateAddressValue}
     >
-      &#10006;
-    </div>
-    <div>
-      {selectionOffsetText}
+      {#each ADDRESS_RADIX_OPTIONS as { name, value }}
+        <option {value}>{name}</option>
+      {/each}
+    </select>
+  </div>
+  {#if $visableViewports === 'physical' || $visableViewports === 'all'}
+  <div class="hdr physical-header" >
+    <div class={$UIThemeCSSClass + ' hd'}>Physical</div>
+    <div class={$UIThemeCSSClass + ' measure physical-viewport-header'}>
+      {#if $displayRadix === RADIX_OPTIONS.Binary}
+        {#each offsetLine as offset}
+          <div class="physical-addr-seg binary" 
style:min-width={byteDivWidthFromRadix($displayRadix)}>
+            <div>{offset}</div>
+            <div>{bitIndexStr}</div>
+          </div>
+        {/each}
+      {:else}
+        {#each offsetLine as offset}
+          <div class="physical-addr-seg" 
style:min-width={byteDivWidthFromRadix($displayRadix)}>
+            {offset}
+          </div>
+        {/each}
+      {/if}
     </div>
-  {:else}
-    <div>
-      <sub
-        ><i
-          >To edit multiple bytes, highlight (by clicking and dragging) a
-          selection of bytes</i
-        ></sub
-      >
+  </div>
+  {/if}
+  {#if $visableViewports === 'logical' || $visableViewports === 'all'}
+  <div class="hdr logical-header" >
+    <div class={$UIThemeCSSClass + ' hd'}>Logical</div>
+    <div 
+      class={$UIThemeCSSClass + ' measure logical logical-viewport-header'}
+      style:align-items={$displayRadix === RADIX_OPTIONS.Binary ? 'flex-end' : 
'normal'} 
+    >
+      {#each offsetLine as offset}
+        <div 
+          class="logical-addr-seg"
+          
style:min-width={byteDivWidthFromRadix(RADIX_OPTIONS.Hexadecimal)}>{offset}</div>
+      {/each}
     </div>
+  </div>
   {/if}
 </div>
 
 <style lang="scss">
-  div.physical-addr-seg,
-  div.logical-addr-seg {
+  div.hdr {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+  div.hdr .measure {
+    flex-direction: row;
+  }
+  div.headers {
+    display: flex;
+    flex-direction: row;
+  }
+  div.logical-addr-seg,
+  div.physical-addr-seg {
     writing-mode: vertical-lr;
     text-orientation: upright;
     cursor: default;
+    border-width: 0 2px 0 2px;
+    border-style: solid;
+    border-color: transparent;
   }
   div.logical-addr-seg {
-    width: 24px;
+    width: 20px;
   }
-  div.div.physical-addr-seg.binary {
+  div.physical-addr-seg.binary {
     writing-mode: horizontal-tb;
     text-orientation: sideways;
   }
   div.physical-viewport-header {
-    padding-left: 4px;
+    padding-left: 2px;
+  }
+  div.physical-viewport-header ,
+  div.logical-viewport-header {
+    display: flex;
+  }
+  div.physical-header,
+  div.logical-header,
+  div.address-header {
+    background: #2f3e4f;
+  }
+  div.logical-header,
+  div.physical-header {
+    border: 1px solid transparent;
   }
 </style>
diff --git a/src/svelte/src/components/Editors/DataEditor.svelte 
b/src/svelte/src/components/Editors/DataEditor.svelte
index f8d51b1..b80784f 100644
--- a/src/svelte/src/components/Editors/DataEditor.svelte
+++ b/src/svelte/src/components/Editors/DataEditor.svelte
@@ -20,8 +20,11 @@ limitations under the License.
     editMode,
     selectionDataStore,
     regularSizedFile,
+    viewport,
+    selectionSize,
+    addressRadix,
   } from '../../stores'
-  import { EditByteModes } from '../../stores/configuration'
+  import { EditByteModes, type RadixValues } from '../../stores/configuration'
   import { UIThemeCSSClass } from '../../utilities/colorScheme'
   import { createEventDispatcher } from 'svelte'
   import ContentControls from 
'../DataDisplays/Fieldsets/ContentControls.svelte'
@@ -29,6 +32,21 @@ limitations under the License.
   import DataView from '../DataDisplays/Fieldsets/DataView.svelte'
   const eventDispatcher = createEventDispatcher()
 
+  let selectionOffsetText: string
+  $: selectionOffsetText = setSelectionOffsetInfo(
+    'Selection',
+    $viewport.fileOffset + $selectionDataStore.startOffset,
+    $viewport.fileOffset + $selectionDataStore.endOffset,
+    $selectionSize,
+    $addressRadix
+  )
+
+  let displayTextEditorArea: boolean
+  $: displayTextEditorArea = $editMode === EditByteModes.Multiple && 
($selectionDataStore.active || !$regularSizedFile)
+
+  function clearDataDisplays() {
+    eventDispatcher('clearDataDisplays')
+  }
   function handleEditorEvent(event: Event) {
     switch (event.type) {
       case 'input':
@@ -49,26 +67,82 @@ limitations under the License.
     }
     eventDispatcher('handleEditorEvent', event)
   }
+  export function setSelectionOffsetInfo(
+    from: string,
+    start: number,
+    end: number,
+    size: number,
+    radix: RadixValues
+  ): string {
+    return `${from} [${start.toString(radix)} - ${end.toString(
+      radix
+    )}] Size: ${size.toString(radix)} `
+  }
 </script>
 
-<div class="editView" id="edit_view">
-  {#if $editMode === EditByteModes.Multiple && ($selectionDataStore.active || 
!$regularSizedFile)}
-    <textarea
-      class={$UIThemeCSSClass}
-      id="selectedContent"
-      contenteditable="true"
-      on:keyup|nonpassive={handleEditorEvent}
-      on:click={handleEditorEvent}
-      on:input={handleEditorEvent}
-      bind:value={$editorSelection}
-    />
+<div 
+  class="editView" 
+  id="edit_view"
+  style:justify-content={displayTextEditorArea ? 'flex-end' : 'flex-start'}
+>
+  <div class="hdr editor-header" >
+    <div class={$UIThemeCSSClass + ' hd'}>Editor</div>
+    <div class={$UIThemeCSSClass + " measure selection-content"}>
+      {#if $selectionDataStore.active && $editMode !== EditByteModes.Single}
+        <!-- svelte-ignore a11y-no-static-element-interactions -->
+        <div
+          class="clear-selection"
+          title="Clear selection data"
+          on:click={clearDataDisplays}
+          on:keypress={clearDataDisplays}
+        >
+          &#10006;
+        </div>
+        <div>
+          {selectionOffsetText}
+        </div>
+      {:else}
+        <sub
+          ><i
+            >To edit multiple bytes, highlight (by clicking and dragging) a
+            selection of bytes</i
+          ></sub
+        >
+      {/if}
+    </div>
+  </div>
+    {#if displayTextEditorArea}
+      <textarea
+        class={$UIThemeCSSClass}
+        id="selectedContent"
+        contenteditable="true"
+        on:keyup|nonpassive={handleEditorEvent}
+        on:click={handleEditorEvent}
+        on:input={handleEditorEvent}
+        bind:value={$editorSelection}
+      />
+  
+      <FlexContainer>
+        <ContentControls on:applyChanges />
+      </FlexContainer>
+    {:else}
+      <FlexContainer>
+        <DataView on:applyChanges />
+      </FlexContainer>
+    {/if}
 
-    <FlexContainer>
-      <ContentControls on:applyChanges />
-    </FlexContainer>
-  {:else}
-    <FlexContainer>
-      <DataView on:applyChanges />
-    </FlexContainer>
-  {/if}
 </div>
+
+<style lang="scss">
+  div.hdr {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    background: #2f3e4f;
+    border: 0;
+  }
+  .selection-content {
+    display: flex;
+    width: 100%;
+  }
+</style>
diff --git a/src/svelte/src/components/Header/fieldsets/Settings.svelte 
b/src/svelte/src/components/Header/fieldsets/Settings.svelte
index 5a0c841..214b02a 100644
--- a/src/svelte/src/components/Header/fieldsets/Settings.svelte
+++ b/src/svelte/src/components/Header/fieldsets/Settings.svelte
@@ -24,12 +24,11 @@ limitations under the License.
     displayRadix,
     editorEncoding,
     editorActionsAllowed,
-    bytesPerRow,
   } from '../../../stores'
   import FlexContainer from '../../layouts/FlexContainer.svelte'
   import { UIThemeCSSClass } from '../../../utilities/colorScheme'
+  import ViewportVisibilityIcon from 
'../../Icons/ViewportVisibilityIcon.svelte'
 
-  $: $bytesPerRow = $displayRadix === RADIX_OPTIONS.Binary ? 8 : 16
 </script>
 
 <fieldset>
@@ -76,6 +75,7 @@ limitations under the License.
     </FlexContainer>
 
     <hr />
+    <ViewportVisibilityIcon dimension={20}/>
   </FlexContainer>
 </fieldset>
 
diff --git a/src/svelte/src/components/Icons/ViewportVisibilityIcon.svelte 
b/src/svelte/src/components/Icons/ViewportVisibilityIcon.svelte
new file mode 100644
index 0000000..363e24a
--- /dev/null
+++ b/src/svelte/src/components/Icons/ViewportVisibilityIcon.svelte
@@ -0,0 +1,214 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<script lang='ts'>
+  import Tooltip from "../layouts/Tooltip.svelte"
+  import { bytesPerRow, dataDislayLineAmount, visableViewports } from 
"../../stores"
+  import { BYTES_PER_ROW_MAX_LINE_NUM, type BytesPerRow } from 
"../../stores/configuration"
+
+    export let dimension: number = 20
+    const defaultDimension = 20
+    const minDimension = 15
+    const maxDimension = 30
+
+    let width  = valid_dimensions()
+    let height = valid_dimensions()
+    let minWidth = pixel_string(minDimension)
+    let minHeight = pixel_string(minDimension)
+    let maxWidth = pixel_string(maxDimension)
+    let maxHeight = pixel_string(maxDimension)
+
+    let selectionsDisplay = {
+        viewports: false,
+        bytesPerRow: false
+    }
+
+    function valid_dimensions(): string {
+        return (dimension > maxDimension || dimension < minDimension) 
+            ? pixel_string(defaultDimension)
+            : pixel_string(dimension)
+    }
+    function pixel_string(value: number): string {
+        return value.toString() + 'px'
+    }
+    function set_bytes_per_row(bytesPerRowSelection: BytesPerRow) {
+        if($dataDislayLineAmount > 
BYTES_PER_ROW_MAX_LINE_NUM[bytesPerRowSelection])
+            $dataDislayLineAmount = 
BYTES_PER_ROW_MAX_LINE_NUM[bytesPerRowSelection]
+        
+        $bytesPerRow = bytesPerRowSelection
+    }
+</script>
+
+<span class="icon-container">
+    <!-- svelte-ignore a11y-click-events-have-key-events -->
+    <Tooltip alwaysEnabled={true} description="Viewports visible: 
{$visableViewports}">
+        <span class="setting-icon" 
+            style:width 
+            style:height
+            style:min-width={ minWidth }
+            style:min-height={ minHeight }
+            style:max-width={ maxWidth }
+            style:max-height={ maxHeight }
+            on:click={() => { selectionsDisplay.viewports = 
selectionsDisplay.viewports ? false : true }}
+        >
+            <span
+                style:background-color={ $visableViewports === 'all' || 
$visableViewports === 'physical' ? 'white' : ''}
+                class="viewport physical" 
+            />
+            <span
+                style:background-color={ $visableViewports === 'all' || 
$visableViewports === 'logical' ? 'white' : ''} 
+                class="viewport logical" 
+            />
+        </span>
+    </Tooltip>
+    {#if selectionsDisplay.viewports}
+    <span 
+        class='material-symbols-outlined'
+        style:width=15px
+        style:font-size=20px
+    >chevron_right
+    </span>
+    <span class="selections-container">
+        <Tooltip alwaysEnabled={true} description="physical">
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
+            <span 
+                class="setting-icon selection physical"
+                on:click={ () => { $visableViewports = 'physical' }}    
+            >
+                <span class="viewport" style:background-color={'white'}></span>
+                <span class="viewport"></span>
+            </span>            
+        </Tooltip>
+
+        <Tooltip alwaysEnabled={true} description="logical">
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
+        <span 
+            class="setting-icon selection logical"
+            on:click={ () => { $visableViewports = 'logical' }}   
+        >
+            <span class="viewport"></span>
+            <span class="viewport" style:background-color={'white'}></span>
+        </span>
+        </Tooltip>
+        <Tooltip alwaysEnabled={true} description="all">
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
+        <span 
+            class="setting-icon selection all"
+            on:click={ () => { $visableViewports = 'all' }}   
+        >
+            <span class="viewport" style:background-color={'white'}></span>
+            <span class="viewport" style:background-color={'white'}></span>
+        </span>
+        </Tooltip>
+    </span>
+    {/if}
+</span>
+
+<!-- BPR Setting Icon -->
+<span class="icon-container">
+    <!-- svelte-ignore a11y-click-events-have-key-events -->
+    <Tooltip alwaysEnabled={true} description="Bytes per row: {$bytesPerRow}">
+        <span class="setting-icon" 
+            style:width 
+            style:height
+            style:min-width={ minWidth }
+            style:min-height={ minHeight }
+            style:max-width={ maxWidth }
+            style:max-height={ maxHeight }
+            on:click={() => { selectionsDisplay.bytesPerRow = 
selectionsDisplay.bytesPerRow ? false : true }}
+        >
+            <span 
+                class="material-symbols-outlined"
+                style:display=flex
+                style:align-content=center
+                style:justify-content=center
+                style:font-size=20px    
+            >power_input</span>
+        </span>
+    </Tooltip>
+    {#if selectionsDisplay.bytesPerRow}
+    <span 
+        class='material-symbols-outlined'
+        style:width=15px
+        style:font-size=20px
+    >chevron_right
+    </span>
+    <span class="selections-container">
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
+        <span 
+            class="setting-icon selection "
+            on:click={ () => { set_bytes_per_row(8) }}    
+        >8
+        </span>
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
+        <span 
+            class="setting-icon selection "
+            on:click={ () => { set_bytes_per_row(16) }}   
+        >16
+        </span>
+        <!-- svelte-ignore a11y-click-events-have-key-events -->
+        <span 
+            class="setting-icon selection "
+            on:click={ () => { set_bytes_per_row(24) }}   
+        >24
+        </span>
+    </span>
+    {/if}
+</span>
+
+<style lang="scss">
+    .icon-container {
+        margin: 2px 0;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        cursor: pointer;
+    }
+    .setting-icon {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        overflow: hidden;
+        border: 1px solid white;
+        border-radius: 4px;
+        padding: 2px;
+    }
+    .selection {
+        width: 15px;
+        height: 15px;
+        cursor: pointer;
+    }
+    .viewport {
+        border: 1px solid white;
+        border-radius: 2px;
+        margin: 1px;
+        width: 50%;
+    }
+    .selections-container{
+        display: flex;
+        flex-direction: row;
+        justify-content: space-evenly;
+        min-width: 85px;
+    }
+    .icon {
+        display: flex;
+        height: 100%;
+        font-size: x-large;
+        align-items: center;
+        justify-content: center;
+    }
+</style>
diff --git a/src/svelte/src/components/Inputs/Buttons/Button.svelte 
b/src/svelte/src/components/Inputs/Buttons/Button.svelte
index bfa5929..644df14 100644
--- a/src/svelte/src/components/Inputs/Buttons/Button.svelte
+++ b/src/svelte/src/components/Inputs/Buttons/Button.svelte
@@ -24,6 +24,7 @@ limitations under the License.
   export let fn: (event?: Event) => void
   export let disabledBy = false
   export let width = ''
+  export let fixedWidth = ''
 
   onMount(() => {
     collapseContent = shouldCollapseContent()
@@ -51,6 +52,7 @@ limitations under the License.
       class={$UIThemeCSSClass + ' collapsed'}
       disabled={disabledBy}
       on:click={!disabledBy ? fn : () => {}}
+      style:width={fixedWidth}
     >
       <FlexContainer
         --dir="row"
@@ -68,7 +70,7 @@ limitations under the License.
       class={$UIThemeCSSClass}
       disabled={disabledBy}
       on:click={!disabledBy ? fn : () => {}}
-      style:width
+      style:width = { fixedWidth.length > 0 ? fixedWidth : width }
     >
       <FlexContainer
         --dir="row"
diff --git a/src/svelte/src/components/Main.svelte 
b/src/svelte/src/components/Main.svelte
index c0291de..ea80ee0 100644
--- a/src/svelte/src/components/Main.svelte
+++ b/src/svelte/src/components/Main.svelte
@@ -21,16 +21,32 @@ limitations under the License.
 </script>
 
 <main class="dataEditor">
-  <DisplayHeader on:clearDataDisplays />
-  <DataViewports
-    on:clearDataDisplays
-    on:scrolledToTop
-    on:scrolledToEnd
-    on:applyChanges
-    on:handleEditorEvent
-    on:scrollBoundary
-    on:traverse-file
-    on:seek
-  />
-  <DataEditor on:applyChanges on:handleEditorEvent />
+  <div class="data-viewports">
+    <DisplayHeader on:clearDataDisplays/>
+    <DataViewports
+      on:clearDataDisplays
+      on:scrolledToTop
+      on:scrolledToEnd
+      on:applyChanges
+      on:handleEditorEvent
+      on:scrollBoundary
+      on:traverse-file
+      on:seek
+    />    
+  </div>
+  <div class="data-editor">
+    <DataEditor on:applyChanges on:handleEditorEvent on:clearDataDisplays/>
+  </div>
 </main>
+
+<style lang="scss">
+  .data-viewports {
+    display: flex;
+    flex-direction: column;
+  }
+  .data-editor {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+  }
+</style>
diff --git a/src/svelte/src/components/dataEditor.svelte 
b/src/svelte/src/components/dataEditor.svelte
index c4984bf..2387bbd 100644
--- a/src/svelte/src/components/dataEditor.svelte
+++ b/src/svelte/src/components/dataEditor.svelte
@@ -29,13 +29,13 @@ limitations under the License.
     requestable,
     selectionDataStore,
     selectionSize,
-    viewportNumLinesDisplayed,
     dataFeedLineTop,
     SelectionData_t,
     dataFeedAwaitRefresh,
     viewport,
     searchQuery,
     regularSizedFile,
+    dataDislayLineAmount,
   } from '../stores'
   import {
     CSSThemeClass,
@@ -48,8 +48,8 @@ limitations under the License.
   import Main from './Main.svelte'
   import {
     EditByteModes,
-    VIEWPORT_SCROLL_INCREMENT,
     type BytesPerRow,
+    VIEWPORT_SCROLL_INCREMENT,
   } from '../stores/configuration'
   import ServerMetrics from './ServerMetrics/ServerMetrics.svelte'
   import {
@@ -87,12 +87,15 @@ limitations under the License.
     bytesPerRow: BytesPerRow,
     viewportStartOffset: number = $viewport.fileOffset
   ): number {
-    const nearestBPRdivisibleOffset = byte_count_divisible_offset(
-      offset - viewportStartOffset,
+    const nearestBPRdivisibleTargetFileOffset = byte_count_divisible_offset(
+      offset,
       bytesPerRow
     )
-    const offsetLineNumInViewport = nearestBPRdivisibleOffset / bytesPerRow
-    return offsetLineNumInViewport
+    const nearestBPRdivisibleViewportFileOffset = byte_count_divisible_offset(
+      viewportStartOffset,
+      bytesPerRow
+    ) 
+    return (nearestBPRdivisibleTargetFileOffset - 
nearestBPRdivisibleViewportFileOffset) / bytesPerRow
   }
 
   function fetchable_content(offset: number): boolean {
@@ -127,8 +130,7 @@ limitations under the License.
 
     $dataFeedAwaitRefresh = true
 
-    offsetArg = byte_count_divisible_offset(offsetArg, $bytesPerRow)
-    const fetchOffset = Math.max(0, offsetArg - VIEWPORT_SCROLL_INCREMENT)
+    const fetchOffset = Math.max(0, byte_count_divisible_offset(offsetArg - 
(VIEWPORT_SCROLL_INCREMENT-$bytesPerRow), $bytesPerRow))
 
     $dataFeedLineTop = offset_to_viewport_line_number(
       offsetArg,
@@ -141,7 +143,7 @@ limitations under the License.
       data: {
         scrollOffset: fetchOffset,
         bytesPerRow: $bytesPerRow,
-        numLinesDisplayed: $viewportNumLinesDisplayed,
+        numLinesDisplayed: $dataDislayLineAmount,
       },
     })
     clearDataDisplays()
@@ -321,6 +323,7 @@ limitations under the License.
 
 <!-- svelte-ignore css-unused-selector -->
 <style lang="scss">
+
   div.test {
     display: flex;
     flex-wrap: wrap;
diff --git a/src/svelte/src/components/globalStyles.css 
b/src/svelte/src/components/globalStyles.css
index 33f6d4c..0a52458 100644
--- a/src/svelte/src/components/globalStyles.css
+++ b/src/svelte/src/components/globalStyles.css
@@ -278,15 +278,10 @@ div.hide-scrollbar::-webkit-scrollbar {
 
 /* Global Style Classes */
 .dataEditor {
-  display: grid;
-  grid-template-columns: 110px max-content max-content auto;
-  grid-template-rows: max-content auto;
-  /*noinspection CssUnresolvedCustomProperty*/
+  display: flex;
   font-family: 'Red Hat Mono';
 }
-/* display of binary encoded data takes more space in the physical view */
 .dataEditor.binary {
-  /* I think this should be 16em instead of 10em for 16 characters, but that 
didn't work */
   grid-template-columns: max-content max-content 10em auto;
 }
 .dataEditor div {
@@ -316,8 +311,6 @@ div.hide-scrollbar::-webkit-scrollbar {
   display: flex;
   font-family: 'Red Hat Mono';
   height: 25pt;
-  border-width: 0 0 2px 0;
-  border-style: solid;
 }
 .dataEditor div.measure.dark {
   display: flex;
@@ -341,7 +334,8 @@ div.hide-scrollbar::-webkit-scrollbar {
 .dataEditor div.measure span {
   align-self: flex-end;
 }
-.dataEditor div.measure div {
+.dataEditor div.measure div,
+.dataEditor div.measure sub {
   display: flex;
   flex-direction: column;
   justify-content: center;
@@ -405,18 +399,15 @@ div.hide-scrollbar::-webkit-scrollbar {
   color: #02060b;
 }
 .dataEditor div.editView {
-  display: grid;
-  grid-template-columns: 1fr;
-  grid-template-rows: 75%;
-  grid-row-start: 3;
-  grid-row-end: 5;
-  gap: 10pt;
-  overflow-x: hidden;
-  word-break: break-all;
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-end;
+  height: 100%;
   margin-left: 4px;
 }
 .dataEditor div.editView textarea {
   width: auto;
+  height: 100%;
 }
 .dataEditor textarea.selectedContent {
   background: #2c2c2c;
@@ -428,6 +419,9 @@ div.hide-scrollbar::-webkit-scrollbar {
   float: right;
   max-width: 110px;
 }
+div.viewport-hdr-content {
+  height: 35px;
+}
 
 .tooltip {
   position: relative;
diff --git a/src/svelte/src/stores/configuration.ts 
b/src/svelte/src/stores/configuration.ts
index 37e3595..21035de 100644
--- a/src/svelte/src/stores/configuration.ts
+++ b/src/svelte/src/stores/configuration.ts
@@ -19,7 +19,7 @@ export type Radixes = 'Hexadecimal' | 'Decimal' | 'Octal' | 
'Binary'
 
 export type RadixValues = 16 | 10 | 8 | 2
 
-export type BytesPerRow = 16 | 8
+export type BytesPerRow = 16 | 8 | 24
 
 export enum EditByteModes {
   Single = 'single',
@@ -108,11 +108,13 @@ export const UNPRINTABLE_CHAR_STAND_IN = 
String.fromCharCode(9617)
 
 // Number of bytes to for the viewport to populate
 export const VIEWPORT_CAPACITY_MAX = 16 * 64 // 1024, Ωedit maximum viewport 
size is 1048576 (1024 * 1024)
-
-// Offset shift amount on viewport data fetch
 export const VIEWPORT_SCROLL_INCREMENT = VIEWPORT_CAPACITY_MAX / 2
 
-// Number of bytes to display in the viewport
-export const NUM_LINES_DISPLAYED = 20
+export const BYTES_PER_ROW_MAX_LINE_NUM: { [k in BytesPerRow]: number } = {
+  16: Math.floor(VIEWPORT_SCROLL_INCREMENT / 16),
+  8: Math.floor(VIEWPORT_SCROLL_INCREMENT / 8),
+  24: Math.floor(VIEWPORT_SCROLL_INCREMENT / 24),
+}
 
+// Number of bytes to display in the viewport
 export const DATA_PROFILE_MAX_LENGTH = 10_000_000
diff --git a/src/svelte/src/stores/index.ts b/src/svelte/src/stores/index.ts
index 5c7eaee..6c1cca9 100644
--- a/src/svelte/src/stores/index.ts
+++ b/src/svelte/src/stores/index.ts
@@ -43,6 +43,7 @@ import {
   type RadixValues,
   type BytesPerRow,
   EditActionRestrictions,
+  VIEWPORT_CAPACITY_MAX,
 } from './configuration'
 
 export class SelectionData_t {
@@ -117,7 +118,6 @@ export const rerenderActionElements = writable(false)
 
 // Viewport properties
 export const viewport = new ViewportDataStore_t()
-export const viewportNumLinesDisplayed = writable(20)
 
 export const bytesPerRow = writable(16 as BytesPerRow)
 export const editingByte = writable(false)
@@ -147,6 +147,10 @@ export const sizeHumanReadable = writable(false)
 // tracks the start and end offsets of the current selection
 export const selectionDataStore = new SelectionData()
 
+export const dataDislayLineAmount = writable(20)
+
+export type VisibleViewports = 'physical' | 'logical' | 'all'
+export const visableViewports = writable('all' as VisibleViewports)
 // Can the user's selection derive both edit modes?
 export const regularSizedFile = derived(fileMetrics, ($fileMetrics) => {
   return $fileMetrics.computedSize >= 2
@@ -288,7 +292,7 @@ export const editedByteIsOriginalByte = derived(
   ([$editorSelection, $selectedByte, $focusedViewportId]) => {
     return $focusedViewportId === 'logical'
       ? $editorSelection === $selectedByte.text
-      : $editorSelection.toLowerCase() === $selectedByte.text.toLowerCase()
+      : $editorSelection.toLowerCase() === $selectedByte.text!.toLowerCase()
   }
 )
 


Reply via email to