changeset 326:612238b3f3e8 am-2

all 4 samples now working in Qt, SDL and wasm
author am@osimis.io
date Tue, 16 Oct 2018 12:57:38 +0200
parents 37ab9d83dc9b
children 8716176ff7f0
files Applications/Samples/CMakeLists.txt Applications/Samples/Qt/SampleMainWindow.cpp Applications/Samples/Qt/SampleMainWindow.h Applications/Samples/Qt/SampleMainWindow.ui Applications/Samples/Qt/SampleMainWindowWithButtons.cpp Applications/Samples/Qt/SampleMainWindowWithButtons.h Applications/Samples/Qt/SampleMainWindowWithButtons.ui Applications/Samples/Qt/SampleQtApplicationRunner.h Applications/Samples/SampleApplicationBase.h Applications/Samples/SampleMainWasm.cpp Applications/Samples/SimpleViewerApplicationSingleFile.h Applications/Samples/SingleFrameApplication.h Applications/Samples/SingleFrameEditorApplication.h Applications/Samples/Web/index.html Applications/Samples/Web/simple-viewer-single-file.ts Applications/Samples/Web/simple-viewer-single-file.tsconfig.json Applications/Samples/Web/single-frame-editor.html Applications/Samples/Web/single-frame-editor.ts Applications/Samples/Web/single-frame-editor.tsconfig.json Applications/Samples/Web/single-frame.html Applications/Samples/Web/single-frame.ts Applications/Samples/Web/single-frame.tsconfig.json Applications/Samples/Web/tsconfig-simple-viewer-single-file.json Applications/Samples/build-web.sh
diffstat 24 files changed, 446 insertions(+), 139 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/CMakeLists.txt	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/CMakeLists.txt	Tue Oct 16 12:57:38 2018 +0200
@@ -95,6 +95,8 @@
     ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h
     ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui
     )
 endif()
 
--- a/Applications/Samples/Qt/SampleMainWindow.cpp	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/Qt/SampleMainWindow.cpp	Tue Oct 16 12:57:38 2018 +0200
@@ -32,36 +32,13 @@
   namespace Samples
   {
 
-    SampleMainWindow::SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, OrthancStone::Samples::SampleApplicationBase& stoneSampleApplication, QWidget *parent) :
+    SampleMainWindow::SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent) :
       QStoneMainWindow(context, parent),
       ui_(new Ui::SampleMainWindow),
       stoneSampleApplication_(stoneSampleApplication)
     {
       ui_->setupUi(this);
       SetCentralStoneWidget(ui_->cairoCentralWidget);
-
-#if QT_VERSION >= 0x050000
-      connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindow::tool1Clicked);
-      connect(ui_->toolButton2, &QToolButton::clicked, this, &SampleMainWindow::tool2Clicked);
-      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindow::pushButton1Clicked);
-      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindow::pushButton2Clicked);
-#else
-      connect(ui_->toolButton1, SIGNAL(clicked()), this, SLOT(tool1Clicked()));
-      connect(ui_->toolButton2, SIGNAL(clicked()), this, SLOT(tool2Clicked()));
-      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked()));
-      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton2Clicked()));
-#endif
-
-      std::string pushButton1Name;
-      std::string pushButton2Name;
-      std::string tool1Name;
-      std::string tool2Name;
-      stoneSampleApplication_.GetButtonNames(pushButton1Name, pushButton2Name, tool1Name, tool2Name);
-
-      ui_->toolButton1->setText(QString::fromStdString(tool1Name));
-      ui_->toolButton2->setText(QString::fromStdString(tool2Name));
-      ui_->pushButton1->setText(QString::fromStdString(pushButton1Name));
-      ui_->pushButton2->setText(QString::fromStdString(pushButton2Name));
     }
 
     SampleMainWindow::~SampleMainWindow()
@@ -69,25 +46,5 @@
       delete ui_;
     }
 
-    void SampleMainWindow::tool1Clicked()
-    {
-      stoneSampleApplication_.OnTool1Clicked();
-    }
-
-    void SampleMainWindow::tool2Clicked()
-    {
-      stoneSampleApplication_.OnTool2Clicked();
-    }
-
-    void SampleMainWindow::pushButton1Clicked()
-    {
-      stoneSampleApplication_.OnPushButton1Clicked();
-    }
-
-    void SampleMainWindow::pushButton2Clicked()
-    {
-      stoneSampleApplication_.OnPushButton2Clicked();
-    }
-
   }
 }
--- a/Applications/Samples/Qt/SampleMainWindow.h	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/Qt/SampleMainWindow.h	Tue Oct 16 12:57:38 2018 +0200
@@ -32,7 +32,7 @@
   namespace Samples
   {
 
-    class SampleApplicationBase;
+    class SampleSingleCanvasApplicationBase;
 
     class SampleMainWindow : public QStoneMainWindow
     {
@@ -40,17 +40,11 @@
 
     private:
       Ui::SampleMainWindow*   ui_;
-      SampleApplicationBase&  stoneSampleApplication_;
+      SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
 
     public:
-      explicit SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, SampleApplicationBase& stoneSampleApplication, QWidget *parent = 0);
+      explicit SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent = 0);
       ~SampleMainWindow();
-
-    private slots:
-      void tool1Clicked();
-      void tool2Clicked();
-      void pushButton1Clicked();
-      void pushButton2Clicked();
     };
   }
 }
--- a/Applications/Samples/Qt/SampleMainWindow.ui	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/Qt/SampleMainWindow.ui	Tue Oct 16 12:57:38 2018 +0200
@@ -38,7 +38,7 @@
    <property name="layoutDirection">
     <enum>Qt::LeftToRight</enum>
    </property>
-   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
     <property name="sizeConstraint">
      <enum>QLayout::SetDefaultConstraint</enum>
     </property>
@@ -52,52 +52,6 @@
       </property>
      </widget>
     </item>
-    <item>
-     <widget class="QGroupBox" name="horizontalGroupBox">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>100</height>
-       </size>
-      </property>
-      <property name="maximumSize">
-       <size>
-        <width>16777215</width>
-        <height>100</height>
-       </size>
-      </property>
-      <layout class="QHBoxLayout" name="horizontalLayout">
-       <item>
-        <widget class="QToolButton" name="toolButton1">
-         <property name="text">
-          <string>tool1</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QToolButton" name="toolButton2">
-         <property name="text">
-          <string>tool2</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButton1">
-         <property name="text">
-          <string>action1</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButton2">
-         <property name="text">
-          <string>action2</string>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-    </item>
    </layout>
   </widget>
   <widget class="QMenuBar" name="menubar">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,93 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "SampleMainWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_SampleMainWindowWithButtons.h>
+#include "../../Applications/Samples/SampleApplicationBase.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    SampleMainWindowWithButtons::SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent) :
+      QStoneMainWindow(context, parent),
+      ui_(new Ui::SampleMainWindowWithButtons),
+      stoneSampleApplication_(stoneSampleApplication)
+    {
+      ui_->setupUi(this);
+      SetCentralStoneWidget(ui_->cairoCentralWidget);
+
+#if QT_VERSION >= 0x050000
+      connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool1Clicked);
+      connect(ui_->toolButton2, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool2Clicked);
+      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton1Clicked);
+      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton2Clicked);
+#else
+      connect(ui_->toolButton1, SIGNAL(clicked()), this, SLOT(tool1Clicked()));
+      connect(ui_->toolButton2, SIGNAL(clicked()), this, SLOT(tool2Clicked()));
+      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked()));
+      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton2Clicked()));
+#endif
+
+      std::string pushButton1Name;
+      std::string pushButton2Name;
+      std::string tool1Name;
+      std::string tool2Name;
+      stoneSampleApplication_.GetButtonNames(pushButton1Name, pushButton2Name, tool1Name, tool2Name);
+
+      ui_->toolButton1->setText(QString::fromStdString(tool1Name));
+      ui_->toolButton2->setText(QString::fromStdString(tool2Name));
+      ui_->pushButton1->setText(QString::fromStdString(pushButton1Name));
+      ui_->pushButton2->setText(QString::fromStdString(pushButton2Name));
+    }
+
+    SampleMainWindowWithButtons::~SampleMainWindowWithButtons()
+    {
+      delete ui_;
+    }
+
+    void SampleMainWindowWithButtons::tool1Clicked()
+    {
+      stoneSampleApplication_.OnTool1Clicked();
+    }
+
+    void SampleMainWindowWithButtons::tool2Clicked()
+    {
+      stoneSampleApplication_.OnTool2Clicked();
+    }
+
+    void SampleMainWindowWithButtons::pushButton1Clicked()
+    {
+      stoneSampleApplication_.OnPushButton1Clicked();
+    }
+
+    void SampleMainWindowWithButtons::pushButton2Clicked()
+    {
+      stoneSampleApplication_.OnPushButton2Clicked();
+    }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Qt/SampleMainWindowWithButtons.h	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,56 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+#pragma once
+
+#include "../../Qt/QCairoWidget.h"
+#include "../../Qt/QStoneMainWindow.h"
+
+namespace Ui 
+{
+  class SampleMainWindowWithButtons;
+}
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    class SampleSingleCanvasWithButtonsApplicationBase;
+
+    class SampleMainWindowWithButtons : public QStoneMainWindow
+    {
+      Q_OBJECT
+
+    private:
+      Ui::SampleMainWindowWithButtons*   ui_;
+      SampleSingleCanvasWithButtonsApplicationBase&  stoneSampleApplication_;
+
+    public:
+      explicit SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent = 0);
+      ~SampleMainWindowWithButtons();
+
+    private slots:
+      void tool1Clicked();
+      void tool2Clicked();
+      void pushButton1Clicked();
+      void pushButton2Clicked();
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Qt/SampleMainWindowWithButtons.ui	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SampleMainWindowWithButtons</class>
+ <widget class="QMainWindow" name="SampleMainWindowWithButtons">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>903</width>
+    <height>634</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="baseSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="QCairoWidget" name="cairoCentralWidget">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>500</height>
+       </size>
+      </property>
+     </widget>
+    </item>
+    <item>
+     <widget class="QGroupBox" name="horizontalGroupBox">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>100</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>16777215</width>
+        <height>100</height>
+       </size>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QToolButton" name="toolButton1">
+         <property name="text">
+          <string>tool1</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="toolButton2">
+         <property name="text">
+          <string>tool2</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton1">
+         <property name="text">
+          <string>action1</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton2">
+         <property name="text">
+          <string>action2</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>903</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCairoWidget</class>
+   <extends>QGraphicsView</extends>
+   <header location="global">QCairoWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- a/Applications/Samples/Qt/SampleQtApplicationRunner.h	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/Qt/SampleQtApplicationRunner.h	Tue Oct 16 12:57:38 2018 +0200
@@ -22,7 +22,6 @@
 #pragma once
 
 #include "../../Qt/QtStoneApplicationRunner.h"
-#include "SampleMainWindow.h"
 
 #if ORTHANC_ENABLE_QT != 1
 #error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
@@ -37,7 +36,7 @@
     protected:
       virtual void InitializeMainWindow(OrthancStone::NativeStoneApplicationContext& context)
       {
-        window_.reset(new SampleMainWindow(context, dynamic_cast<OrthancStone::Samples::SampleApplicationBase&>(application_)));
+        window_.reset(application_.CreateQtMainWindow());
       }
     public:
       SampleQtApplicationRunner(MessageBroker& broker,
--- a/Applications/Samples/SampleApplicationBase.h	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/SampleApplicationBase.h	Tue Oct 16 12:57:38 2018 +0200
@@ -22,6 +22,17 @@
 #pragma once
 
 #include "../../Applications/IStoneApplication.h"
+#include "../../Framework/Widgets/LayerWidget.h"
+
+#if ORTHANC_ENABLE_WASM==1
+#include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
+#include "../../Platforms/Wasm/Defaults.h"
+#endif
+
+#if ORTHANC_ENABLE_QT==1
+#include "Qt/SampleMainWindow.h"
+#include "Qt/SampleMainWindowWithButtons.h"
+#endif
 
 namespace OrthancStone
 {
@@ -31,6 +42,8 @@
     {
     protected:
       BaseCommandBuilder commandBuilder_;
+      LayerWidget*       mainWidget_;   // ownership is transfered to the application context
+
     public:
       virtual void Initialize(StoneApplicationContext* context,
                               IStatusBar& statusBar,
@@ -43,6 +56,31 @@
         return "Stone of Orthanc - Sample";
       }
 
+      virtual BaseCommandBuilder& GetCommandBuilder() {return commandBuilder_;}
+
+      virtual void Finalize() {}
+      virtual IWidget* GetCentralWidget() {return mainWidget_;}
+
+#if ORTHANC_ENABLE_WASM==1
+      // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter
+
+      virtual void InitializeWasm()
+      {
+        AttachWidgetToWasmViewport("canvas", mainWidget_);
+      }
+
+      virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker)
+      {
+        return new WasmPlatformApplicationAdapter(broker, *this);
+      }
+#endif
+
+    };
+
+    // this application actually works in Qt and WASM
+    class SampleSingleCanvasWithButtonsApplicationBase : public SampleApplicationBase
+    {
+public:
       virtual void OnPushButton1Clicked() {}
       virtual void OnPushButton2Clicked() {}
       virtual void OnTool1Clicked() {}
@@ -59,8 +97,24 @@
         tool2 = "tool2";
       }
 
-      virtual BaseCommandBuilder& GetCommandBuilder() {return commandBuilder_;}
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() {
+        return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+      }
+#endif
+
+    };
 
+    // this application actually works in SDL and WASM
+    class SampleSingleCanvasApplicationBase : public SampleApplicationBase
+    {
+public:
+
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() {
+        return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+      }
+#endif
     };
   }
 }
--- a/Applications/Samples/SampleMainWasm.cpp	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/SampleMainWasm.cpp	Tue Oct 16 12:57:38 2018 +0200
@@ -26,7 +26,12 @@
 #include "SampleList.h"
 
 
-OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) {
-  
+OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) 
+{
   return new SampleApplication(broker);
+}
+
+OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application)
+{
+  return dynamic_cast<SampleApplication*>(application)->CreateWasmApplicationAdapter(broker);
 }
\ No newline at end of file
--- a/Applications/Samples/SimpleViewerApplicationSingleFile.h	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h	Tue Oct 16 12:57:38 2018 +0200
@@ -36,9 +36,6 @@
 #include "../../Platforms/Wasm/Defaults.h"
 #endif
 
-#if ORTHANC_ENABLE_QT==1
-#include "Qt/SampleMainWindow.h"
-#endif
 #include <Core/Logging.h>
 
 namespace OrthancStone
@@ -46,7 +43,7 @@
   namespace Samples
   {
     class SimpleViewerApplication :
-        public SampleApplicationBase,
+        public SampleSingleCanvasWithButtonsApplicationBase,
         public IObserver
     {
     private:
@@ -218,7 +215,6 @@
       std::unique_ptr<ThumbnailInteractor>  thumbnailInteractor_;
       LayoutWidget*                   mainLayout_;
       LayoutWidget*                   thumbnailsLayout_;
-      LayerWidget*                    mainWidget_;
       std::vector<LayerWidget*>       thumbnails_;
       std::map<std::string, std::vector<std::string>> instancesIdsPerSeriesId_;
       std::map<std::string, Json::Value> seriesTags_;
@@ -243,9 +239,6 @@
 //        DeclareIgnoredMessage(MessageType_Widget_ContentChanged);
       }
 
-      virtual void Finalize() {}
-      virtual IWidget* GetCentralWidget() {return mainLayout_;}
-
       virtual void DeclareStartupOptions(boost::program_options::options_description& options)
       {
         boost::program_options::options_description generic("Sample options");
@@ -412,11 +405,6 @@
       }
 #endif
 
-#if ORTHANC_ENABLE_QT==1
-      virtual QStoneMainWindow* CreateQtMainWindow() {
-        return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
-      }
-#endif
     };
 
 
--- a/Applications/Samples/SingleFrameApplication.h	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/SingleFrameApplication.h	Tue Oct 16 12:57:38 2018 +0200
@@ -33,7 +33,7 @@
   namespace Samples
   {
     class SingleFrameApplication :
-      public SampleApplicationBase,
+      public SampleSingleCanvasApplicationBase,
       public IObserver
     {
     private:
@@ -183,7 +183,6 @@
         mainWidget_->SetDefaultView();
       }
       
-      LayerWidget*                          mainWidget_;   // ownership is transfered to the application context
       std::unique_ptr<Interactor>           mainWidgetInteractor_;
       std::unique_ptr<OrthancApiClient>     orthancApiClient_;
 
@@ -254,9 +253,6 @@
         mainWidgetInteractor_.reset(new Interactor(*this));
         mainWidget_->SetInteractor(*mainWidgetInteractor_);
       }
-
-      virtual void Finalize() {}
-      virtual IWidget* GetCentralWidget() {return mainWidget_;}
     };
 
 
--- a/Applications/Samples/SingleFrameEditorApplication.h	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Tue Oct 16 12:57:38 2018 +0200
@@ -33,7 +33,7 @@
   namespace Samples
   {
     class SingleFrameEditorApplication :
-        public SampleApplicationBase,
+        public SampleSingleCanvasApplicationBase,
         public IObserver
     {
       enum Tools
@@ -153,7 +153,6 @@
         mainWidget_->SetDefaultView();
       }
       
-      LayerWidget*                          mainWidget_;   // ownership is transfered to the application context
       std::unique_ptr<Interactor>           mainWidgetInteractor_;
       std::unique_ptr<OrthancApiClient>     orthancApiClient_;
       Tools                                 currentTool_;
@@ -217,8 +216,6 @@
         mainWidget_->SetInteractor(*mainWidgetInteractor_);
       }
 
-      virtual void Finalize() {}
-      virtual IWidget* GetCentralWidget() {return mainWidget_;}
 
       void Invert()
       {
--- a/Applications/Samples/Web/index.html	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/Web/index.html	Tue Oct 16 12:57:38 2018 +0200
@@ -14,7 +14,9 @@
 
 <body>
     <ul>
-      <li><a href="simple-viewer/simple-viewer.html">Simple Viewer Project</a></li>
+      <li><a href="simple-viewer/simple-viewer.html">Simple Viewer Project (you may add ?studyId=XXX in the url)</a></li>
+      <li><a href="single-frame.html?instance=XXX">Single frame application (you must replace XXX by a valid instance id in the url)</a></li>
+      <li><a href="single-frame-editor.html?instance=XXX">Single frame editor application (you must replace XXX by a valid instance id in the url)</a></li>
       <li><a href="simple-viewer-single-file.html">Simple Viewer Single file (to be replaced by other samples)</a></li>
     </ul>
 </body>
--- a/Applications/Samples/Web/simple-viewer-single-file.ts	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/Web/simple-viewer-single-file.ts	Tue Oct 16 12:57:38 2018 +0200
@@ -1,6 +1,6 @@
 ///<reference path='../../../Platforms/Wasm/wasm-application-runner.ts'/>
 
-InitializeWasmApplication("OrthancStoneSimpleViewer", "/orthanc");
+InitializeWasmApplication("OrthancStoneSimpleViewerSingleFile", "/orthanc");
 
 function SelectTool(toolName: string) {
     var command = {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/simple-viewer-single-file.tsconfig.json	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,9 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+        "outFile": "../build-web/app-simple-viewer-single-file.js"
+    },
+    "include" : [
+        "simple-viewer-single-file.ts"
+    ]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/single-frame-editor.html	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,22 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Simple Viewer</title>
+    <link href="samples-styles.css" rel="stylesheet" />
+
+<body>
+  <div>
+    <canvas id="canvas" data-width-ratio="100" data-height-ratio="100"></canvas>
+  </div>
+  <script type="text/javascript" src="app-single-frame-editor.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/single-frame-editor.ts	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,3 @@
+///<reference path='../../../Platforms/Wasm/wasm-application-runner.ts'/>
+
+InitializeWasmApplication("OrthancStoneSingleFrameEditor", "/orthanc");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/single-frame-editor.tsconfig.json	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,9 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+        "outFile": "../build-web/app-single-frame-editor.js"
+    },
+    "include" : [
+        "single-frame-editor.ts"
+    ]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/single-frame.html	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,22 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Simple Viewer</title>
+    <link href="samples-styles.css" rel="stylesheet" />
+
+<body>
+  <div>
+    <canvas id="canvas" data-width-ratio="100" data-height-ratio="100"></canvas>
+  </div>
+  <script type="text/javascript" src="app-single-frame.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/single-frame.ts	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,3 @@
+///<reference path='../../../Platforms/Wasm/wasm-application-runner.ts'/>
+
+InitializeWasmApplication("OrthancStoneSingleFrame", "/orthanc");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Web/single-frame.tsconfig.json	Tue Oct 16 12:57:38 2018 +0200
@@ -0,0 +1,9 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+        "outFile": "../build-web/app-single-frame.js"
+    },
+    "include" : [
+        "single-frame.ts"
+    ]
+}
\ No newline at end of file
--- a/Applications/Samples/Web/tsconfig-simple-viewer-single-file.json	Tue Oct 16 11:30:00 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-{
-    "extends" : "./tsconfig-samples",
-    "compilerOptions": {
-        "outFile": "../build-web/app-simple-viewer-single-file.js"
-    },
-    "include" : [
-        "simple-viewer-single-file.ts"
-    ]
-}
\ No newline at end of file
--- a/Applications/Samples/build-web.sh	Tue Oct 16 11:30:00 2018 +0200
+++ b/Applications/Samples/build-web.sh	Tue Oct 16 12:57:38 2018 +0200
@@ -16,10 +16,22 @@
 
 # build simple-viewer-single-file (obsolete project)
 cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir
-tsc --allowJs --project $samplesRootDir/Web/tsconfig-simple-viewer-single-file.json
+tsc --allowJs --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json
 cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js  $outputDir
 cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm  $outputDir
 
+# build single-frame
+cp $samplesRootDir/Web/single-frame.html $outputDir
+tsc --allowJs --project $samplesRootDir/Web/single-frame.tsconfig.json
+cp $currentDir/build-wasm/OrthancStoneSingleFrame.js  $outputDir
+cp $currentDir/build-wasm/OrthancStoneSingleFrame.wasm  $outputDir
+
+# build single-frame-editor
+cp $samplesRootDir/Web/single-frame-editor.html $outputDir
+tsc --allowJs --project $samplesRootDir/Web/single-frame-editor.tsconfig.json
+cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.js  $outputDir
+cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm  $outputDir
+
 # build simple-viewer project
 mkdir -p $outputDir/simple-viewer/
 cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/