changeset 2020:88673f50d7b9

added key bindings to change active study and series
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 03 Dec 2022 11:59:20 +0100
parents fe9999d6a636
children 1458f3bcb991
files Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/index.html
diffstat 3 files changed, 98 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/NEWS	Sat Dec 03 09:22:19 2022 +0100
+++ b/Applications/StoneWebViewer/NEWS	Sat Dec 03 11:59:20 2022 +0100
@@ -2,6 +2,11 @@
 ===============================
 
 * Click-drag is available on the vertical slider
+* Added key bindings:
+  - Left/right arrows to change the active frame
+  - Up/down arrows to change the active series
+  - Page up/down to change the active study
+  - Space bar to play/pause videos
 * New URL argument "menu" to change the layout of the list of studies/series
 * The first series to be loaded is now automatically opened
 * Annotations are grouped into one submenu for narrow screens
--- a/Applications/StoneWebViewer/WebApplication/app.js	Sat Dec 03 09:22:19 2022 +0100
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Sat Dec 03 11:59:20 2022 +0100
@@ -125,6 +125,17 @@
 }
 
 
+function LookupIndexOfResource(array, tag, value) {
+  for (var i = 0; i < array.length; i++) {
+    if (array[i].tags[tag] == value) {
+      return i;
+    }
+  }
+  
+  return -1;
+}
+
+
 /**
  * Enable support for tooltips in Bootstrap. This function must be
  * called after each modification to the DOM that introduces new
@@ -351,9 +362,9 @@
       }
     });
 
-    window.addEventListener('CineSwitch', function(args) {
+    window.addEventListener('KeyCineSwitch', function(args) {
       if (that.active) {
-        that.CineSwitch();
+        that.KeyCineSwitch();
       }
     });
   },
@@ -395,7 +406,7 @@
       this.cineIncrement = 1;
       this.UpdateCine();
     },
-    CineSwitch: function() {
+    KeyCineSwitch: function() {
       if (this.cineIncrement != 0) {
         this.CinePause();
       } else {
@@ -589,8 +600,30 @@
         });
       }
     },
+
+    GetActiveViewportSeriesTags: function() {
+      if (this.activeViewport == 1 &&
+          this.viewport1Content.virtualSeriesId === undefined) {
+        return this.viewport1Content.series.tags;
+      }
+      else if (this.activeViewport == 2 &&
+               this.viewport2Content.virtualSeriesId === undefined) {
+        return this.viewport2Content.series.tags;
+      }
+      else if (this.activeViewport == 3 &&
+               this.viewport3Content.virtualSeriesId === undefined) {
+        return this.viewport3Content.series.tags;
+      }
+      else if (this.activeViewport == 4 &&
+               this.viewport4Content.virtualSeriesId === undefined) {
+        return this.viewport4Content.series.tags;
+      }
+      else {
+        return null;
+      }
+    },
     
-    GetActiveSeries: function() {
+    GetActiveSeriesInstanceUid: function() {
       var s = [];
 
       if ('tags' in this.viewport1Content.series)
@@ -1196,7 +1229,48 @@
         .catch(function (error) {
           alert('Cannot find the study in Orthanc');
         });
-      
+    },
+
+    ChangeActiveSeries: function(offset) {
+      var seriesTags = this.GetActiveViewportSeriesTags();
+      if (seriesTags !== null) {
+        var studyIndex = LookupIndexOfResource(this.studies, STUDY_INSTANCE_UID, seriesTags[STUDY_INSTANCE_UID]);
+        if (studyIndex != -1) {
+          var seriesInStudyIndices = this.studies[studyIndex].series;
+          for (var i = 0; i < seriesInStudyIndices.length; i++) {
+            if (this.series[seriesInStudyIndices[i]].tags[SERIES_INSTANCE_UID] == seriesTags[SERIES_INSTANCE_UID]) {
+              var next = i + offset;
+              if (next >= 0 &&
+                  next < seriesInStudyIndices.length) {
+                this.SetViewportSeriesInstanceUid(this.activeViewport, this.series[seriesInStudyIndices[next]].tags[SERIES_INSTANCE_UID]);
+              }
+              break;
+            }
+          }
+        }
+      }
+    },
+
+    ChangeActiveStudy: function(offset) {
+      var seriesTags = this.GetActiveViewportSeriesTags();
+      if (seriesTags !== null) {
+        var studyIndex = LookupIndexOfResource(this.studies, STUDY_INSTANCE_UID, seriesTags[STUDY_INSTANCE_UID]);
+        if (studyIndex != -1) {
+          var next = studyIndex + offset;
+          if (next >= 0 &&
+              next < this.studies.length) {
+            var nextStudy = this.studies[next];
+            if (nextStudy.series.length > 0) {
+              var seriesIndex = nextStudy.series[0];
+              if (this.series[seriesIndex].virtualSeries !== null) {
+                this.ClickVirtualSeries(seriesIndex, this.series[seriesIndex].virtualSeries);
+              } else {
+                this.ClickSeries(seriesIndex);
+              }
+            }
+          }
+        }
+      }
     }
   },
   
@@ -1276,15 +1350,25 @@
 
         case 'Up':
         case 'ArrowUp':
+          that.ChangeActiveSeries(-1);
+          break
+          
+        case 'Down':
+        case 'ArrowDown':
+          that.ChangeActiveSeries(1);
           break;
 
-        case 'Down':
-        case 'ArrowDown':
+        case 'PageUp':
+          that.ChangeActiveStudy(-1);
+          break
+          
+        case 'PageDown':
+          that.ChangeActiveStudy(1);
           break;
 
         case ' ':
         case 'Space':
-          dispatchEvent(new CustomEvent('CineSwitch', { }));
+          dispatchEvent(new CustomEvent('KeyCineSwitch', { }));
           event.preventDefault();
           break;
 
--- a/Applications/StoneWebViewer/WebApplication/index.html	Sat Dec 03 09:22:19 2022 +0100
+++ b/Applications/StoneWebViewer/WebApplication/index.html	Sat Dec 03 11:59:20 2022 +0100
@@ -217,7 +217,7 @@
                         <!-- Series without multiple multiframe instances -->
                         <span v-for="seriesIndex in study.series">
                           <li class="wvSerieslist__seriesItem"
-                              v-bind:class="{ highlighted : GetActiveSeries().includes(series[seriesIndex].tags[SERIES_INSTANCE_UID]), 'wvSerieslist__seriesItem--list' : leftMode != 'grid', 'wvSerieslist__seriesItem--grid' : leftMode == 'grid' }"
+                              v-bind:class="{ highlighted : GetActiveSeriesInstanceUid().includes(series[seriesIndex].tags[SERIES_INSTANCE_UID]), 'wvSerieslist__seriesItem--list' : leftMode != 'grid', 'wvSerieslist__seriesItem--grid' : leftMode == 'grid' }"
                               v-on:dragstart="SeriesDragStart($event, seriesIndex)"
                               v-on:click="ClickSeries(seriesIndex)"
                               v-if="series[seriesIndex].virtualSeries === null">