ecere/gui/Window: Prevent uninitialized values if base Window methods not overridden...
[sdk] / ecere / src / gui / Window.ec
index bacd3df..5965634 100644 (file)
@@ -25,6 +25,10 @@ import "EditBox"
 import "DataBox"
 import "ToolTip"
 
+#if defined(__WIN32__)
+import "Win32Interface"
+#endif
+
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
 import "Desktop3D"
 #endif
@@ -38,6 +42,37 @@ import "MessageBox"
 import "WindowList"
 import "i18n"
 
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
+#define property _property
+#define new _new
+#define class _class
+#define uint _uint
+
+#define Window    X11Window
+#define Cursor    X11Cursor
+#define Font      X11Font
+#define Display   X11Display
+#define Time      X11Time
+#define KeyCode   X11KeyCode
+#define Picture   X11Picture
+
+#include <X11/Xutil.h>
+
+#undef Window
+#undef Cursor
+#undef Font
+#undef Display
+#undef Time
+#undef KeyCode
+#undef Picture
+
+#undef uint
+#undef new
+#undef property
+#undef class
+
+#endif
+
 // Had to define this here for native decorations support, because the menu bar is part of total decoration's size, but not part of the system decorations
 #ifdef HIGH_DPI
 define skinMenuHeight = 40;
@@ -487,15 +522,24 @@ private class HotKeySlot : struct
    Key key;
 };
 
+public struct TouchPointerInfo
+{
+   int id;
+   Point point;
+   float size, pressure;
+};
+
+public enum TouchPointerEvent { move, up, down, pointerUp, pointerDown };
+
 public class Window
 {
 private:
-   class_data char * icon;
+   class_data const char * icon;
    class_no_expansion
    class_default_property caption;
    // class_initialize GuiApplication::Initialize;
    class_designer FormDesigner;
-   class_property char * icon
+   class_property const char * icon
    {
       set { class_data(icon) = value; }
       get { return class_data(icon); }
@@ -568,6 +612,27 @@ private:
       OldLink slave;
       ResPtr ptr;
 
+#if !defined(__EMSCRIPTEN__)
+      if(fileMonitor)
+      {
+         int i, lockCount = guiApp.lockMutex.lockCount;
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
+         if(xGlobalDisplay)
+            XUnlockDisplay(xGlobalDisplay);
+#endif
+
+         for(i = 0; i < lockCount; i++)
+            guiApp.lockMutex.Release();
+         delete fileMonitor;
+         for(i = 0; i < lockCount; i++)
+            guiApp.lockMutex.Wait();
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
+         if(xGlobalDisplay)
+            XLockDisplay(xGlobalDisplay);
+#endif
+      }
+#endif
+
       if(parent)
       {
          stopwatching(parent, font);
@@ -606,7 +671,7 @@ private:
       delete order;
       /////////////////////////////////
 
-      while(ptr = resources.first)
+      while((ptr = resources.first))
       {
          delete ptr.resource;
          resources.Delete(ptr);
@@ -625,7 +690,7 @@ private:
          *&child.parent = null;
       }
 
-      while(slave = slaves.first)
+      while((slave = slaves.first))
       {
          // Don't want property here
          *&((Window)slave.data).master = null;
@@ -651,7 +716,9 @@ private:
       delete statusBar;
 
       OnDestroyed();
+#if !defined(__EMSCRIPTEN__)
       delete mutex;
+#endif
       delete icon;
 
       if(((subclass(Window))_class).pureVTbl)
@@ -688,23 +755,25 @@ private:
          tempExtents[3].Free(null);
          delete tempExtents;
       }
+
+      delete controller;
    }
 
 //#if !defined(ECERE_VANILLA)
-   char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
+   const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
    {
       if(this == activeDesigner)
          return "(Desktop)";
       else
       {
-         char * name = property::name;
+         const char * name = property::name;
          return name ? name : "";
       }
    }
 //#endif
 
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE)
-   bool OnGetDataFromString(char * string)
+   bool OnGetDataFromString(const char * string)
    {
       FormDesigner designer = (FormDesigner)activeDesigner.classDesigner;
       if(string[0])
@@ -743,7 +812,7 @@ private:
       {
          int x = absPosition.x + clientStart.x;
          int y = absPosition.y + clientStart.y;
-         if(rootWindow.nativeDecorations && rootWindow.windowHandle)
+         if(rootWindow.nativeDecorations && rootWindow.windowHandle && !is3D)
          {
             x -= rootWindow.clientStart.x;
             y -= rootWindow.clientStart.y - (rootWindow.hasMenuBar ? skinMenuHeight : 0);
@@ -755,13 +824,21 @@ private:
 
             if(rootWindow.is3D)
             {
-               x += rootWindow.parent.clientStart.x;
-               y += rootWindow.parent.clientStart.y;
+               int dx = rootWindow.parent.parent.clientStart.x;
+               int dy = rootWindow.parent.parent.clientStart.y;
+               if(!rootWindow.parent.nativeDecorations)
+               {
+                  dx += rootWindow.parent.clientStart.x;
+                  dy += rootWindow.parent.clientStart.y;
+               }
+               x += dx;
+               y += dy;
+
                /*
-               mox.left += rootWindow.parent.clientStart.x;
-               mox.top += rootWindow.parent.clientStart.y;
-               mox.right += rootWindow.parent.clientStart.x;
-               mox.bottom += rootWindow.parent.clientStart.y;
+               mox.left += dx;
+               mox.top += dy;
+               mox.right += dx;
+               mox.bottom += dy;
                */
             }
          }
@@ -791,24 +868,34 @@ private:
       {
          int x = absPosition.x;
          int y = absPosition.y;
-         if(rootWindow.nativeDecorations && rootWindow.windowHandle)
+         if(rootWindow.nativeDecorations && rootWindow.windowHandle && !is3D)
          {
             x -= rootWindow.clientStart.x;
             y -= rootWindow.clientStart.y - (rootWindow.hasMenuBar ? skinMenuHeight : 0);
          }
+#if !defined(__EMSCRIPTEN__)
          if(!guiApp.fullScreenMode || is3D)
+#endif
          {
             x -= rootWindow.absPosition.x;
             y -= rootWindow.absPosition.y;
             if(rootWindow.is3D)
             {
-               x += rootWindow.parent.clientStart.x;
-               y += rootWindow.parent.clientStart.y;
+               int dx = rootWindow.parent.parent.clientStart.x;
+               int dy = rootWindow.parent.parent.clientStart.y;
+               if(!rootWindow.parent.nativeDecorations)
+               {
+                  dx += rootWindow.parent.clientStart.x;
+                  dy += rootWindow.parent.clientStart.y;
+               }
+               x += dx;
+               y += dy;
+
                /*
-               mox.left += rootWindow.parent.clientStart.x;
-               mox.top += rootWindow.parent.clientStart.y;
-               mox.right += rootWindow.parent.clientStart.x;
-               mox.bottom += rootWindow.parent.clientStart.y;
+               mox.left += dx;
+               mox.top += dy;
+               mox.right += dx;
+               mox.bottom += dy;
                */
             }
          }
@@ -949,12 +1036,12 @@ private:
       int ph = parent ? parent.clientSize.h : 0;
       int w = sizeAnchor.size.w, h = sizeAnchor.size.h;
       int x = anchor.left.distance, y = anchor.top.distance;
-      float ex, ey;
+      float ex = 0, ey = 0;
       MinMaxValue ew = 0, eh = 0;
       int numCascade;
       float cascadeW, cascadeH;
       int numTiling;
-      int tilingW, tilingH, tilingSplit, tilingLastH;
+      int tilingW, tilingH, tilingSplit, tilingLastH = 0;
       int addX = 0, addY = 0;
 
       if(parent && rootWindow == this && guiApp && guiApp.interfaceDriver)
@@ -1050,7 +1137,7 @@ private:
          int loX = 0, loY = 0, hiX = pw, hiY = ph;
          for(win = parent.children.first; win; win = win.next)
          {
-            if(!win.isActiveClient && win.visible)
+            if(!win.nonClient && !win.isActiveClient && win.visible)
             {
                Size size = win.size;
                Point pos = win.position;
@@ -1096,13 +1183,23 @@ private:
             if(parent.numIcons) ph -= guiApp.textMode ? 16 : 24;
             if(anchor.left.type == vTiled)
             {
-               tilingH = (int)sqrt(numTiling);
-               tilingW = numTiling / tilingH;
+               if(numTiling)
+               {
+                  tilingH = (int)sqrt(numTiling);
+                  tilingW = numTiling / tilingH;
+               }
+               else
+                  tilingH = tilingW = 0;
             }
             else
             {
-               tilingW = (int)sqrt(numTiling);
-               tilingH = numTiling / tilingW;
+               if(numTiling)
+               {
+                  tilingW = (int)sqrt(numTiling);
+                  tilingH = numTiling / tilingW;
+               }
+               else
+                  tilingH = tilingW = 0;
             }
 
             leftOver = numTiling - tilingH * tilingW;
@@ -1114,19 +1211,30 @@ private:
             else
                tilingSplit = numTiling;
 
-            if(positionID >= tilingSplit)
+            if(tilingW && tilingH)
             {
-               x = xOffset + pw * (tilingSplit / tilingH + (positionID - tilingSplit) / tilingLastH)/tilingW;
-               y = yOffset + ph * ((positionID - tilingSplit) % tilingLastH) / tilingLastH;
-               x2 = xOffset + pw * (tilingSplit/tilingH + (positionID - tilingSplit) / tilingLastH + 1)/tilingW;
-               y2 = yOffset + ph * (((positionID - tilingSplit) % tilingLastH) + 1) / tilingLastH;
+               if(positionID >= tilingSplit)
+               {
+                  x = xOffset + pw * (tilingSplit / tilingH + (positionID - tilingSplit) / tilingLastH)/tilingW;
+                  y = yOffset + ph * ((positionID - tilingSplit) % tilingLastH) / tilingLastH;
+                  x2 = xOffset + pw * (tilingSplit/tilingH + (positionID - tilingSplit) / tilingLastH + 1)/tilingW;
+                  y2 = yOffset + ph * (((positionID - tilingSplit) % tilingLastH) + 1) / tilingLastH;
+               }
+               else
+               {
+                  x = xOffset + pw * (positionID / tilingH) / tilingW;
+                  y = yOffset + ph * (positionID % tilingH) / tilingH;
+                  x2 = xOffset + pw * (positionID / tilingH + 1) / tilingW;
+                  y2 = yOffset + ph * ((positionID % tilingH) + 1) / tilingH;
+               }
             }
             else
             {
-               x = xOffset + pw * (positionID / tilingH) / tilingW;
-               y = yOffset + ph * (positionID % tilingH) / tilingH;
-               x2 = xOffset + pw * (positionID / tilingH + 1) / tilingW;
-               y2 = yOffset + ph * ((positionID % tilingH) + 1) / tilingH;
+               // How can this happen? From ec2 parsing test
+               x = 0;
+               y = 0;
+               x2 = 0;
+               y2 = 0;
             }
             if(guiApp.textMode)
             {
@@ -1602,6 +1710,8 @@ private:
       {
          if(!parent.noAutoScrollArea)
          {
+            // TODO: Review the logic of all this? Each child is going to reposition the parent?
+            /*
             Window child;
             bool found = false;
             for(child = children.first; child; child = child.next)
@@ -1616,7 +1726,8 @@ private:
                   }
                }
             }
-            //if(!found)
+            if(!found)
+            */
             {
                Window parent = this.parent;
                parent.Position(
@@ -1642,7 +1753,7 @@ private:
       int oldCW = clientSize.w, oldCH = clientSize.h;
       bool clientResized, windowResized, windowMoved;
       Window child;
-      bool realResized = size.w != w || size.h != h;
+      //bool realResized = size.w != w || size.h != h;
 
       // TOCHECK: This wasn't in ecere.dll
       //if(!parent) return true;
@@ -1751,7 +1862,11 @@ private:
                   if(display && !display.flags.memBackBuffer && changeRootWindow)
                      guiApp.interfaceDriver.PositionRootWindow(this, x, y, w, h, windowMoved, windowResized); //realResized);
 
-               if(!guiApp.fullScreenMode && this != guiApp.desktop && (windowResized || windowMoved))
+               if(
+#if !defined(__EMSCRIPTEN__)
+                  !guiApp.fullScreenMode &&
+#endif
+                  this != guiApp.desktop && (windowResized || windowMoved))
                   for(child = parent.children.first; child && child != this; child = child.next)
                      if(child.rootWindow)
                         guiApp.interfaceDriver.UpdateRootWindow(child.rootWindow);
@@ -1797,7 +1912,7 @@ private:
                   display.Unlock();
                }
             }
-            if(guiApp.driver && changeRootWindow && windowHandle)
+            if(guiApp.driver && !guiApp.modeSwitching && changeRootWindow && windowHandle)
             {
                if(windowResized || windowMoved)
                   if(!display || display.flags.memBackBuffer)
@@ -1816,7 +1931,9 @@ private:
                   child.display.width = display.width;
                   child.display.height = display.height;
                   child.display.driverData = display.driverData;
+#if !defined(__EMSCRIPTEN__)
                   child.display.mutex = null;
+#endif
                }
             }
          }
@@ -1839,9 +1956,9 @@ private:
       if(guiApp.currentSkin)
       {
          MinMaxValue cw = 0, ch = 0;
-         bool sbvVisible, sbhVisible;
-         int rangeH, rangeV;
-         int positionH, positionV;
+         bool sbvVisible = false, sbhVisible = false;
+         int rangeH = 0, rangeV = 0;
+         int positionH = 0, positionV;
 
          // First get client area with no respect to scroll bars
 
@@ -2312,18 +2429,18 @@ private:
 
       if(hasMaxMin && parent)
       {
-         SkinBitmap skin;
+         //SkinBitmap skin;
          unichar symbol;
          bool (* method)(Window window, Button button, int x, int y, Modifiers mods);
          if(state == maximized)
          {
-            skin = restore;
+            //skin = restore;
             method = RestoreButtonClicked;
             symbol = '\x12';
          }
          else
          {
-            skin = maximize;
+            //skin = maximize;
             method = MaximizeButtonClicked;
             symbol = '\x18';
          }
@@ -2345,18 +2462,18 @@ private:
 
       if(hasMaxMin && parent)
       {
-         SkinBitmap skin;
+         //SkinBitmap skin;
          unichar symbol;
          bool (* method)(Window window, Button button, int x, int y, Modifiers mods);
          if (state == minimized)
          {
-            skin = restore;
+            //skin = restore;
             method = RestoreButtonClicked;
             symbol = '\x12';
          }
          else
          {
-            skin = minimize;
+            //skin = minimize;
             method = MinimizeButtonClicked;
             symbol = '\x19';
          }
@@ -2440,7 +2557,8 @@ private:
       {
          char caption[2048];
          FigureCaption(caption);
-         guiApp.interfaceDriver.SetRootWindowCaption(this, caption);
+         if(guiApp.interfaceDriver)
+            guiApp.interfaceDriver.SetRootWindowCaption(this, caption);
       }
       UpdateDecorations();
       if(parent)
@@ -2451,7 +2569,8 @@ private:
             {
                char caption[2048];
                parent.FigureCaption(caption);
-               guiApp.interfaceDriver.SetRootWindowCaption(parent, caption);
+               if(guiApp.interfaceDriver)
+                  guiApp.interfaceDriver.SetRootWindowCaption(parent, caption);
             }
             else
                parent.UpdateCaption();
@@ -2479,7 +2598,7 @@ private:
       if(menu)
       {
          MenuItem item;
-         bool disabled;
+         //bool disabled;
 
          if(menu)
             menu.Clean(this);
@@ -2494,7 +2613,6 @@ private:
                int id;
                for(id = 0, cycle = activeClient.cycle; cycle && id<10;)
                {
-                  MenuItem item;
                   Window document = cycle.data;
                   if(!document.style.nonClient && document.style.isActiveClient && document.visible)
                   {
@@ -2515,10 +2633,10 @@ private:
 
          if((!previous && activeClient) || !activeClient)
          {
-            if(!activeClient)
+            /*if(!activeClient)
                disabled = true;
             else
-               disabled = false;
+               disabled = false;*/
             item = menu.FindItem(MenuWindowCloseAll, 0);
             if(item) item.disabled = false;
             item = menu.FindItem(MenuWindowNext, 0);
@@ -2565,7 +2683,7 @@ private:
 
    void _ShowDecorations(Box box, bool post)
    {
-      if(rootWindow == this && nativeDecorations) return;
+      if(rootWindow == this && nativeDecorations && !is3D) return;
       if(visible && this != guiApp.desktop)
       {
          Surface surface = RedrawFull(box);
@@ -2645,6 +2763,7 @@ private:
          // Default Settings
          surface.TextFont(usedFont.font);
          surface.TextOpacity(false);
+         surface.outlineColor = black;
 
          OnRedraw(surface);
 
@@ -2673,6 +2792,7 @@ private:
 
          surface.TextFont(usedFont.font);
          surface.TextOpacity(false);
+         surface.outlineColor = black;
 
          OnDrawOverChildren(surface);
 
@@ -2721,7 +2841,6 @@ private:
 
    void ComputeRenderAreaNonOpaque(Extent dirtyExtent, Extent overDirtyExtent, Extent backBufferUpdate)
    {
-      bool opaque = IsOpaque();
       Window child;
       int offsetX = absPosition.x - rootWindow.absPosition.x, offsetY = absPosition.y - rootWindow.absPosition.y;
       if(rootWindow.nativeDecorations && rootWindow.windowHandle)
@@ -2733,7 +2852,6 @@ private:
 
       for(child = children.last; child; child = child.prev)
       {
-         ColorAlpha background = *(ColorAlpha *)&child.background;
          bool opaque = child.IsOpaque();
          if(!child.style.hidden && child.created && !child.is3D && child.rootWindow)
          {
@@ -3097,6 +3215,7 @@ private:
 
          clipExtent.AddBox(box);
 
+         display.Lock(true);
          display.StartUpdate();
 
          if(!rootWindow.fullRender)
@@ -3145,6 +3264,7 @@ private:
          }
 
          display.EndUpdate();
+         display.Unlock();
          dirtyBack.Empty();
 
          dirty = false;
@@ -3254,8 +3374,7 @@ private:
                Window ancestor = null;
                if(isD)
                   for(ancestor = last; ancestor && ancestor.parent != this; ancestor = ancestor.parent);
-               // for(child = isD ? (last.previous == children.first ? null : last.previous) : children.last; child; child = child.prev)
-               for(child = isD ? (ancestor.previous == children.first ? null : ancestor) : children.last; child; child = child.prev)
+               for(child = (isD ? (last != ancestor ? ancestor : ancestor.previous) : children.last); child; child = child.prev)
                {
                   if(child != statusBar && child.rootWindow == rootWindow)
                   {
@@ -3266,8 +3385,7 @@ private:
                }
                if(clickThru)
                {
-                  //for(child = isD ? (last.previous == children.first ? null : last.previous) : children.last; child; child = child.prev)
-                  for(child = isD ? (ancestor.previous == children.first ? null : ancestor.previous) : children.last; child; child = child.prev)
+                  for(child = (isD ? (last != ancestor ? ancestor : ancestor.previous) : children.last); child; child = child.prev)
                   {
                      if(child != statusBar && child.rootWindow == rootWindow)
                      {
@@ -3340,7 +3458,7 @@ private:
             if(rootWindow.active)
                guiApp.interfaceDriver.StopMoving(rootWindow);
          }
-         ReleaseCapture();
+         guiApp.windowCaptured.ReleaseCapture();
          guiApp.resizeX = guiApp.resizeY = guiApp.resizeEndX = guiApp.resizeEndY = false;
          guiApp.windowIsResizing = false;
       }
@@ -3477,7 +3595,7 @@ private:
 
          // TESTING THIS HERE
          UpdateDecorations();
-         if(result = OnActivate(active, previous, goOnWithActivation, direct) && *goOnWithActivation && master)
+         if((result = OnActivate(active, previous, goOnWithActivation, direct) && *goOnWithActivation && master))
             result = NotifyActivate(master, this, active, previous);
          else
          {
@@ -3491,7 +3609,7 @@ private:
                this.active = active;
                if(acquiredInput)
                   AcquireInputEx(active);
-               if(active)
+               if(active && isEnabled)
                {
                   if(caretSize)
                   {
@@ -3561,7 +3679,7 @@ private:
 
    void ConsequentialMouseMove(bool kbMoving)
    {
-      if(rootWindow)
+      if(rootWindow && !noConsequential)
       {
          if(kbMoving || !guiApp.windowMoving)
          {
@@ -3569,7 +3687,8 @@ private:
             int x,y;
             if(rootWindow == guiApp.desktop || rootWindow.parent == guiApp.desktop)
             {
-               guiApp.interfaceDriver.GetMousePosition(&x, &y);
+               if(guiApp.interfaceDriver)
+                  guiApp.interfaceDriver.GetMousePosition(&x, &y);
 
                if(guiApp.windowMoving || rootWindow.GetAtPosition(x, y, true, false, null))
                   rootWindow.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, &mods, true, false);
@@ -3653,7 +3772,7 @@ private:
 
                   if(real)
                   {
-                     bool acquireInput = false;
+                     //bool acquireInput = false;
                      bool maximize =
                         parent.activeChild &&
                         parent.activeChild.state == maximized &&
@@ -3690,7 +3809,7 @@ private:
                               delete this;
                               return false;
                            }
-                           acquireInput = true;
+                           //acquireInput = true;
                         }
                      }
 
@@ -3737,7 +3856,10 @@ private:
                         if(activateParent && parent && !parent.active /*parent != parent.parent.activeChild*/)
                            parent.ActivateEx(true, true, moveInactive, activateRoot, external, externalSwap);
                      }
-                     else if(!guiApp.fullScreenMode)
+                     else
+#if !defined(__EMSCRIPTEN__)
+                     if(!guiApp.fullScreenMode)
+#endif
                      {
                         Window modalRoot = FindModal();
                         if(!modalRoot) modalRoot = this;
@@ -4001,11 +4123,77 @@ private:
       reEntrancy = false;
    }
 
+   public bool MultiTouchMessage(TouchPointerEvent event, Array<TouchPointerInfo> infos, Modifiers * mods, bool consequential, bool activate)
+   {
+      bool result = true;
+      if((infos && infos.count) || (event == up || event == pointerUp))
+      {
+         Window w = null;
+         while(result && w != this)
+         {
+            // TODO: How to handle this?
+            int x = (infos && infos.count) ? infos[0].point.x : 0;
+            int y = (infos && infos.count) ? infos[0].point.y : 0;
+            Window msgWindow = GetAtPosition(x,y, false, true, w);
+            Window window;
+            delete w;
+            w = msgWindow;
+            if(w) incref w;
+            window = (w && !w.disabled) ? w : null;
+
+            if(guiApp.windowCaptured && (guiApp.windowCaptured.rootWindow == this))
+            {
+               if(!guiApp.windowCaptured.isEnabled)
+                  guiApp.windowCaptured.ReleaseCapture();
+               else
+                  window = guiApp.windowCaptured;
+            }
+
+            if(consequential) mods->isSideEffect = true;
+            if(!result || (window && window.destroyed)) window = null;
+
+            if(window)
+            {
+               if(window.OnMultiTouch && !window.disabled)
+               {
+                  Array<TouchPointerInfo> in = null;
+                  if(infos && infos.count)
+                  {
+                     in = { size = infos.size };
+                     memcpy(in.array, infos.array, sizeof(TouchPointerInfo) * infos.size);
+
+                     for(i : in)
+                     {
+                        i.point.x -= (window.absPosition.x + window.clientStart.x);
+                        i.point.y -= (window.absPosition.y + window.clientStart.y);
+
+                        i.point.x = Max(Min(i.point.x, 32767),-32768);
+                        i.point.y = Max(Min(i.point.y, 32767),-32768);
+                     }
+                  }
+
+                  incref window;
+                  if(!window.OnMultiTouch(event, in, *mods))
+                     result = false;
+
+                  delete in;
+                  delete window;
+               }
+            }
+            if(!result || !w || !w.clickThrough)
+               break;
+         }
+         delete w;
+      }
+      return result;
+   }
+
    public bool MouseMessage(uint method, int x, int y, Modifiers * mods, bool consequential, bool activate)
    {
       bool result = true;
       bool wasMoving = guiApp.windowMoving ? true : false;
       bool wasScrolling = guiApp.windowScrolling ? true : false;
+      bool firstPass = true;
       Window w = null;
       while(result && w != this)
       {
@@ -4255,17 +4443,40 @@ private:
             msgWindow.SelectMouseCursor();
          */
 
-         if(guiApp.windowCaptured || trueWindow)
+         if(firstPass && (guiApp.windowCaptured || trueWindow))
          {
             Window prevWindow = guiApp.prevWindow;
-            if(guiApp.prevWindow && trueWindow != guiApp.prevWindow)
+            List<Window> overWindows = guiApp.overWindows;
+            Iterator<Window> it { overWindows };
+
+            while(it.Next())
+            {
+               Window ww = it.data;
+               if(trueWindow != ww && !trueWindow.IsDescendantOf(ww))
+               {
+                  it.pointer = null;
+                  result = ww.OnMouseLeave(*mods);
+                  if(!result) break;
+                  overWindows.TakeOut(ww);
+               }
+            }
+
+            if(result && guiApp.prevWindow && trueWindow != guiApp.prevWindow)
             {
                guiApp.prevWindow.mouseInside = false;
                guiApp.prevWindow = null;
 
-               // Eventually fix this not to include captured?
-               if(!trueWindow.IsDescendantOf(prevWindow) && !prevWindow.OnMouseLeave(*mods))
-                  result = false;
+               if(result)
+               {
+                  if(trueWindow.IsDescendantOf(prevWindow))
+                  {
+                     if(!overWindows.Find(prevWindow))
+                        overWindows.Add(prevWindow);
+                  }
+                  // Eventually fix this not to include captured?
+                  else if(!prevWindow.OnMouseLeave(*mods))
+                     result = false;
+               }
             }
             if(result && trueWindow && !trueWindow.destroyed/* && trueWindow == window*/)
             {
@@ -4289,13 +4500,22 @@ private:
                }
             }
             if(trueWindow && trueWindow._refCount > 1 && !trueWindow.destroyed)
+            {
+               for(wi : guiApp.overWindows; wi == trueWindow)
+               {
+                  OnMouseLeave(0);
+                  guiApp.overWindows.TakeOut(wi);
+                  break;
+               }
                guiApp.prevWindow = trueWindow;
+            }
             else
                guiApp.prevWindow = null;
          }
          SelectMouseCursor();
 
-         if(window && !guiApp.windowMoving && !wasMoving && !wasScrolling)
+         if(window && ((!guiApp.windowMoving && !wasMoving) ||
+            (wasMoving && guiApp.windowMoving && method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp)) && !wasScrolling)
          {
             int clientX = x - (window.absPosition.x + window.clientStart.x);
             int clientY = y - (window.absPosition.y + window.clientStart.y);
@@ -4312,6 +4532,11 @@ private:
                incref window;
                if(!MouseMethod(window, clientX, clientY, *mods))
                   result = false;
+
+#ifdef __ANDROID__
+               if(method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp)
+                  window.OnMouseLeave(*mods);
+#endif
                delete window;
             }
          }
@@ -4324,6 +4549,7 @@ private:
          */
          if(!result || !w || !w.clickThrough)
             break;
+         firstPass = false;
       }
       delete w;
       return result;
@@ -4354,7 +4580,9 @@ private:
 
          if(method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown)
             status = OnSysKeyDown(key, character);
-         else if(method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit)
+         if(status &&
+            (method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown ||
+             method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit))
             status = OnSysKeyHit(key, character);
          else if(method == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp)
             status = OnSysKeyUp(key, character);
@@ -4557,10 +4785,10 @@ private:
                         {
                            if(cycleParent.CycleChildren(!key.shift, false, false, true))
                            {
+                              /*
                               Window child = cycleParent.activeChild;
 
                               // Scroll the window to include the active control
-                              /*
                               if(cycleParent.sbh && !child.style.dontScrollHorz)
                               {
                                  if(child.scrolledPos.x < 0)
@@ -4794,8 +5022,12 @@ private:
       Window child;
 
       // Setup relationship with outside world (bb root || !bb)
+#if defined(__EMSCRIPTEN__)
+      if(this == guiApp.desktop)
+#else
       if((!guiApp.fullScreenMode && parent == guiApp.desktop) || this == guiApp.desktop ||
          (_displayDriver && parent.dispDriver && dispDriver != parent.dispDriver))
+#endif
       {
          rootWindow = this;
          if(!tempExtents)
@@ -4827,19 +5059,24 @@ private:
       bool result = false;
       Window child;
 
+#if defined(__EMSCRIPTEN__)
+      if(this == guiApp.desktop)
+#else
       if((!guiApp.fullScreenMode && parent == guiApp.desktop) || (guiApp.fullScreenMode && (this == guiApp.desktop || (_displayDriver && parent.dispDriver && dispDriver != parent.dispDriver))))
+#endif
       {
          subclass(DisplayDriver) dDriver = (dispDriver && !formDesigner) ? dispDriver : GetDisplayDriver(guiApp.defaultDisplayDriver);
          DisplaySystem displaySystem = dDriver ? dDriver.displaySystem : null;
 
-         windowHandle = dDriver.printer ? null : guiApp.interfaceDriver.CreateRootWindow(this);
+         if(!windowHandle)
+            windowHandle = dDriver.printer ? null : guiApp.interfaceDriver.CreateRootWindow(this);
 
          // This was here, is it really needed?
          //guiApp.interfaceDriver.ActivateRootWindow(this);
 
          if(!displaySystem)
          {
-            displaySystem = DisplaySystem {};
+            displaySystem = DisplaySystem { glCapabilities = glCapabilities };
             if(!displaySystem.Create(dDriver.name, guiApp.fullScreenMode ? windowHandle : windowHandle /*null*/, guiApp.fullScreenMode))
             {
                delete displaySystem;
@@ -4847,7 +5084,7 @@ private:
          }
          if(displaySystem)
          {
-            display = Display { alphaBlend = alphaBlend, useSharedMemory = useSharedMemory, windowDriverData = windowData };
+            display = Display { alphaBlend = alphaBlend, useSharedMemory = useSharedMemory, glCapabilities = glCapabilities, windowDriverData = windowData };
             if(display.Create(displaySystem, windowHandle))
                result = true;
             else
@@ -4932,7 +5169,11 @@ private:
          }
       }
 
-      if(guiApp.fullScreenMode || this != guiApp.desktop)
+      if(
+#if !defined(__EMSCRIPTEN__)
+      guiApp.fullScreenMode ||
+#endif
+         this != guiApp.desktop)
       {
          SetWindowMinimum(&skinMinSize.w, &skinMinSize.h);
          if(display)
@@ -5003,7 +5244,8 @@ private:
 
             for(ptr = resources.first; ptr; ptr = ptr.next)
             {
-               ptr.loaded = display.displaySystem.LoadResource(ptr.resource);
+               if(!ptr.loaded)   // This check prevents a leak in case a watcher on 'font' calls AddResource (ListBox FontResource leak)
+                  ptr.loaded = display.displaySystem.LoadResource(ptr.resource);
             }
             if(setFont)
                AddResource(setFont);
@@ -5163,15 +5405,11 @@ private:
          if(activeChild)
             guiApp.interfaceDriver.ActivateRootWindow(activeChild);
       }
-      /*
-      TODO:
       if(!success)
-         //guiApp.LogErrorCode(IERR_GRAPHICS_LOADING_FAILED, caption);
-         guiApp.LogErrorCode(IERR_GRAPHICS_LOADING_FAILED, class.name);
-      */
+         LogErrorCode(graphicsLoadingFailed, _class.name);
 
       // Do this here to avoid problems on Windows
-      if(stateBackup == maximized)
+      if(rootWindow == this && parent && stateBackup == maximized)
          property::state = maximized;
       return result;
    }
@@ -5421,6 +5659,15 @@ private:
          guiApp.prevWindow = null;
          OnMouseLeave(0);
       }
+      else
+      {
+         for(w : guiApp.overWindows; w == this)
+         {
+            OnMouseLeave(0);
+            guiApp.overWindows.TakeOut(w);
+            break;
+         }
+      }
       if(guiApp.caretOwner == this)
       {
          guiApp.interfaceDriver.SetCaret(0,0,0);
@@ -5764,35 +6011,38 @@ private:
                break;
             case minimized:
             {
-               int maxIcons = parent.clientSize.w / MINIMIZED_WIDTH;
-               Window child;
-               int size = 256;
-               byte * idBuffer = new0 byte[size];
-               int c;
-               for(child = parent.children.first; child; child = child.next)
+               if(hasMinimize)
                {
-                  if(child != this && child.state == minimized)
+                  int maxIcons = parent.clientSize.w / MINIMIZED_WIDTH;
+                  Window child;
+                  int size = 256;
+                  byte * idBuffer = new0 byte[size];
+                  int c;
+                  for(child = parent.children.first; child; child = child.next)
                   {
-                     if(child.iconID > size - 2)
+                     if(child != this && child.state == minimized)
                      {
-                        idBuffer = renew0 idBuffer byte[size*2];
-                        memset(idBuffer + size, 0, size);
-                        size *= 2;
+                        if(child.iconID > size - 2)
+                        {
+                           idBuffer = renew0 idBuffer byte[size*2];
+                           memset(idBuffer + size, 0, size);
+                           size *= 2;
+                        }
+                        idBuffer[child.iconID] = (byte)bool::true;
                      }
-                     idBuffer[child.iconID] = (byte)bool::true;
                   }
-               }
-               for(c = 0; c<size; c++)
-                  if(!idBuffer[c])
-                     break;
-               iconID = c;
-               delete idBuffer;
-               if(style.isActiveClient && !style.hidden)
-                  parent.numIcons++;
+                  for(c = 0; c<size; c++)
+                     if(!idBuffer[c])
+                        break;
+                  iconID = c;
+                  delete idBuffer;
+                  if(style.isActiveClient && !style.hidden)
+                     parent.numIcons++;
 
-               stateAnchor = Anchor { left = (iconID % maxIcons) * MINIMIZED_WIDTH, bottom = (iconID / maxIcons) * (guiApp.textMode ? 16 : 24) };
-               stateSizeAnchor = SizeAnchor { size.w = MINIMIZED_WIDTH };
-               break;
+                  stateAnchor = Anchor { left = (iconID % maxIcons) * MINIMIZED_WIDTH, bottom = (iconID / maxIcons) * (guiApp.textMode ? 16 : 24) };
+                  stateSizeAnchor = SizeAnchor { size.w = MINIMIZED_WIDTH };
+                  break;
+               }
             }
          }
          // TOCHECK: Why was this here?
@@ -5800,13 +6050,15 @@ private:
          //position.y = (ty > 0) ? ty & 0xFFFFF : ty;
          ComputeAnchors(stateAnchor, stateSizeAnchor, &x, &y, &w, &h);
 
-         Position(x, y, w, h, true, true, true, true, false, true);
+         if(state != minimized || hasMinimize)
+            Position(x, y, w, h, true, true, true, true, false, true);
 
          if(!style.inactive && !style.interim && parent && this == parent.activeClient)
             parent.UpdateActiveDocument(null);
       }
 
-      CreateSystemChildren();
+      if(state != minimized || hasMinimize)
+         CreateSystemChildren();
       // ------------------------------------------------------
    }
 
@@ -5849,7 +6101,7 @@ private:
    public bool AcquireInput(bool acquired)
    {
       bool result = true;
-      if(acquiredInput != acquired)
+      if((guiApp.acquiredWindow && acquiredInput) != acquired)
       {
          if(active || (!visible && creationActivation == activate))
             result = AcquireInputEx(acquired);
@@ -5871,7 +6123,7 @@ private:
          if(child.cycle && !child.style.nonClient && child.style.isActiveClient && child.visible)
          {
             DataRow row = listBox.AddRow();
-            row.tag = (int64)child;
+            row.tag = (int64)(intptr)child;
             child.FigureCaption(caption);
             row.SetData(null, caption);
          }
@@ -5882,9 +6134,15 @@ private:
    {
       if(guiApp.driver != null)
       {
+#if !defined(__EMSCRIPTEN__)
          if(guiApp.fullScreenMode && guiApp.desktop.display)
+#else
+         if(true)
+#endif
          {
+#if !defined(__EMSCRIPTEN__)
             guiApp.desktop.mutex.Wait();
+#endif
             guiApp.desktop.display.Lock(true);
 
             Update(extent);
@@ -5908,12 +6166,16 @@ private:
             }
 
             guiApp.desktop.display.Unlock();
+#if !defined(__EMSCRIPTEN__)
             guiApp.desktop.mutex.Release();
+#endif
          }
          else
          {
             Window rootWindow = this.rootWindow;
+#if !defined(__EMSCRIPTEN__)
             rootWindow.mutex.Wait();
+#endif
             display.Lock(true);
 
             Update(extent);
@@ -5921,7 +6183,9 @@ private:
                guiApp.SignalEvent();
             else
             {
+#if !defined(__EMSCRIPTEN__)
                guiApp.waitMutex.Wait();
+#endif
                guiApp.interfaceDriver.Lock(rootWindow);
                if(!rootWindow.style.hidden && rootWindow.dirty)
                {
@@ -5933,10 +6197,14 @@ private:
                   rootWindow.dirty = false;
                }
                guiApp.interfaceDriver.Unlock(rootWindow);
+#if !defined(__EMSCRIPTEN__)
                guiApp.waitMutex.Release();
+#endif
             }
             display.Unlock();
+#if !defined(__EMSCRIPTEN__)
             rootWindow.mutex.Release();
+#endif
          }
       }
    }
@@ -6046,7 +6314,7 @@ private:
             guiApp.interfaceDriver.SetMousePosition(guiApp.windowMovingStart.x, guiApp.windowMovingStart.y);
          else
          {
-            int x, y;
+            int x = 0, y = 0;
             guiApp.interfaceDriver.GetMousePosition(&x, &y);
             guiApp.windowMovingStart.x += x - absPosition.x;
             guiApp.windowMovingStart.y += y - absPosition.y;
@@ -6067,6 +6335,30 @@ private:
       }
    }
 
+   void SetupFileMonitor()
+   {
+#if !defined(__EMSCRIPTEN__)
+      if(!fileMonitor)
+      {
+         fileMonitor = FileMonitor
+         {
+            this, FileChange { modified = true };
+
+            bool OnFileNotify(FileChange action, const char * param)
+            {
+               incref this;
+               fileMonitor.StopMonitoring();
+               if(OnFileModified(action, param))
+                  fileMonitor.StartMonitoring();
+               delete this;
+               return true;
+            }
+         };
+         incref fileMonitor;
+      }
+#endif
+   }
+
 public:
    // normal Methods
    bool Create()
@@ -6077,7 +6369,6 @@ public:
          result = true;
       else if(guiApp && guiApp.driver != null)
       {
-         void * systemParent = null;
          OldLink slaveHolder;
          Window last;
          bool visible = !style.hidden;
@@ -6116,8 +6407,10 @@ public:
                }
          }
 
+#if !defined(__EMSCRIPTEN__)
          if(parent == guiApp.desktop && !mutex)
             mutex = Mutex {};
+#endif
 
          if(style.isDocument)
          {
@@ -6287,13 +6580,17 @@ public:
                         /*if(rootWindow == this)
                            guiApp.interfaceDriver.ActivateRootWindow(this);
                         else*/
-                        if(creationActivation == activate)
+                        if(creationActivation == activate && guiApp.desktop.active)
                            ActivateEx(true, false, true, true, null, null);
-                        else if(creationActivation == flash)
-                           Flash();
+                        else if(creationActivation == activate || creationActivation == flash)
+                        {
+                           MakeActive();
+                           if(this == rootWindow)
+                              Flash();
+                        }
                      }
 
-                     if(!destroyed)
+                     if(!destroyed && !noConsequential)
                         rootWindow.ConsequentialMouseMove(false);
 
                      result = true;
@@ -6344,6 +6641,11 @@ public:
             Box realBox;
 
             // Testing this to avoid repetitve full update to take time...
+            if(rootWindow.fullRender)
+            {
+               rootWindow.dirty = true;
+               return;
+            }
             if(dirtyArea.count == 1)
             {
                BoxItem item = (BoxItem)ACCESS_ITEM(dirtyArea, dirtyArea.first);
@@ -6512,7 +6814,6 @@ public:
 
    void SetScrollArea(int width, int height, bool snapToStep)
    {
-      bool resize = false;
       if(snapToStep)
       {
          int stepX = sbStep.x, stepY = sbStep.y;
@@ -6624,7 +6925,7 @@ public:
       {
          if(state == newState || OnStateChange(newState, mods))
          {
-            WindowState prevState = state;
+            //WindowState prevState = state;
 
             StopMoving();
 
@@ -6791,7 +7092,11 @@ public:
       if(guiApp.desktop.active)
          Activate();
       else if(!active)
-         Flash();
+      {
+         MakeActive();
+         if(this == rootWindow)
+            Flash();
+      }
    }
 
    void Deactivate(void)
@@ -6902,7 +7207,8 @@ public:
          if(/*created && */display)
          {
             display.Lock(false);
-            ptr.loaded = display.displaySystem.LoadResource(resource);
+            if(!ptr.loaded)   // This check prevents a leak in case a watcher on 'font' calls AddResource (ListBox FontResource leak)
+               ptr.loaded = display.displaySystem.LoadResource(resource);
             display.Unlock();
          }
          /*
@@ -6954,7 +7260,7 @@ public:
          caretPos.x = x;
          caretPos.y = y;
          caretSize = size;
-         if(active && !style.interim)
+         if(active && !style.interim && isEnabled)
          {
             if(visible || !guiApp.caretOwner)
                guiApp.caretOwner = size ? this : null;
@@ -7026,7 +7332,7 @@ public:
       }
    }
 
-   private void _SetCaption(char * format, va_list args)
+   private void _SetCaption(const char * format, va_list args)
    {
       if(this)
       {
@@ -7046,7 +7352,7 @@ public:
       }
    }
 
-   /*deprecated*/ void SetText(char * format, ...)
+   /*deprecated*/ void SetText(const char * format, ...)
    {
       va_list args;
       va_start(args, format);
@@ -7054,7 +7360,7 @@ public:
       va_end(args);
    }
 
-   void SetCaption(char * format, ...)
+   void SetCaption(const char * format, ...)
    {
       va_list args;
       va_start(args, format);
@@ -7085,10 +7391,16 @@ public:
             clip.bottom += absPosition.y;
          }
 
-         clip.left += decorations ? 0 : clientStart.x;
-         clip.top += decorations ? 0 : clientStart.y;
-         clip.right += decorations ? 0 : clientStart.x;
-         clip.bottom += decorations ? 0 : clientStart.y;
+         if(!nativeDecorations)
+         {
+            clip.left += decorations ? 0 : clientStart.x;
+            clip.top += decorations ? 0 : clientStart.y;
+            clip.right += decorations ? 0 : clientStart.x;
+            clip.bottom += decorations ? 0 : clientStart.y;
+         }
+
+         if(decorations && this == guiApp.desktop)
+            clip = { 0, 0, guiApp.virtualScreen.w, guiApp.virtualScreen.h };
 
          if(display && display.flags.flipping)
          {
@@ -7265,19 +7577,28 @@ public:
          for(slave = slaves.first; slave; slave = slave.next)
          {
             Window w = slave.data;
-            if((w.parent == this || !w.IsDescendantOf(this)) && !w.CloseConfirmation(true))
+            if(w.parent != this && !w.IsDescendantOf(this) && !w.CloseConfirmation(true))
             {
-               // ((Window)slave.data).CloseConfirmation(true);
                result = false;
                break;
             }
          }
       }
 
+      // Confirm closure of active clients first (We use OnClose() to hide instead of destroy in the IDE)
+      if(result)
+      {
+         for(child = children.first; child; child = child.next)
+            if(child.isActiveClient && !child.CloseConfirmation(true))
+            {
+               result = false;
+               break;
+            }
+      }
       if(result)
       {
          for(child = children.first; child; child = child.next)
-            if(child.master != this && !child.CloseConfirmation(true))
+            if(!child.isActiveClient && !child.CloseConfirmation(true))
             {
                result = false;
                break;
@@ -7316,9 +7637,12 @@ public:
 
    bool MenuFileSave(MenuItem selection, Modifiers mods)
    {
+      SetupFileMonitor();
       if(fileName)
       {
+#if !defined(__EMSCRIPTEN__)
          fileMonitor.fileName = null;
+#endif
          saving = true;
 
          if(OnSaveFile(fileName))
@@ -7326,7 +7650,9 @@ public:
             //if(OnFileModified != Window::OnFileModified)
             {
                saving = false;
+#if !defined(__EMSCRIPTEN__)
                fileMonitor.fileName = fileName;
+#endif
             }
             return true;
          }
@@ -7346,6 +7672,8 @@ public:
       DialogResult result = (DialogResult)bool::true;
       FileDialog fileDialog = saveDialog;
 
+      SetupFileMonitor();
+
       if(!fileDialog)
          fileDialog = FileDialog {};
       if(fileDialog)
@@ -7359,7 +7687,9 @@ public:
             sprintf(filePath, "Untitled %d", documentID);
             fileDialog.filePath = filePath;
          }
+#if !defined(__EMSCRIPTEN__)
          fileMonitor.fileName = null;
+#endif
 
          fileDialog.type = save;
          fileDialog.text = $"Save As";
@@ -7369,7 +7699,7 @@ public:
             fileDialog.master = master.parent ? master : this;
             if(fileDialog.Modal() == ok)
             {
-               char * filePath = fileDialog.filePath;
+               const char * filePath = fileDialog.filePath;
                saving = true;
                if(OnSaveFile(filePath))
                {
@@ -7396,11 +7726,13 @@ public:
                break;
             }
          }
+#if !defined(__EMSCRIPTEN__)
          //if(OnFileModified != Window::OnFileModified && fileName)
          {
             if(fileName)
                fileMonitor.fileName = fileName;
          }
+#endif
          delete fileDialog;
       }
       return (bool)result; // Actually returning result from Yes/NoCancel message box
@@ -7468,11 +7800,10 @@ public:
                         (*&child.normalAnchor).top = y;
                         (*&child.normalAnchor).right.type = none;
                         (*&child.normalAnchor).bottom.type = none;
-
-                        child.normalSizeAnchor.isClientW = false;
-                        child.normalSizeAnchor.isClientH = false;
-                        child.normalSizeAnchor.size.w = w;
-                        child.normalSizeAnchor.size.h = h;
+                        (*&child.normalSizeAnchor).isClientW = false;
+                        (*&child.normalSizeAnchor).isClientH = false;
+                        (*&child.normalSizeAnchor).size.w = w;
+                        (*&child.normalSizeAnchor).size.h = h;
                         child.anchored = false;
                      }
 
@@ -7516,7 +7847,7 @@ public:
 
       for(document = children.first; document; document = next)
       {
-         for(next = document.next; next && !(next.style.isActiveClient; next = next.next);
+         for(next = document.next; next && !next.style.isActiveClient; next = next.next);
          if(document.style.isActiveClient)
             if(!document.Destroy(0) && !document.style.hidden)
                return false;
@@ -7643,10 +7974,10 @@ public:
                         (*&child.normalAnchor).top = y;
                         (*&child.normalAnchor).right.type = none;
                         (*&child.normalAnchor).bottom.type = none;
-                        child.normalSizeAnchor.isClientW = false;
-                        child.normalSizeAnchor.isClientH = false;
-                        child.normalSizeAnchor.size.w = w;
-                        child.normalSizeAnchor.size.h = h;
+                        (*&child.normalSizeAnchor).isClientW = false;
+                        (*&child.normalSizeAnchor).isClientH = false;
+                        (*&child.normalSizeAnchor).size.w = w;
+                        (*&child.normalSizeAnchor).size.h = h;
                         child.anchored = false;
                      }
 
@@ -7708,10 +8039,10 @@ public:
                         (*&child.normalAnchor).top = y;
                         (*&child.normalAnchor).right.type = none;
                         (*&child.normalAnchor).bottom.type = none;
-                        child.normalSizeAnchor.isClientW = false;
-                        child.normalSizeAnchor.isClientH = false;
-                        child.normalSizeAnchor.size.w = w;
-                        child.normalSizeAnchor.size.h = h;
+                        (*&child.normalSizeAnchor).isClientW = false;
+                        (*&child.normalSizeAnchor).isClientH = false;
+                        (*&child.normalSizeAnchor).size.w = w;
+                        (*&child.normalSizeAnchor).size.h = h;
                         child.anchored = false;
                      }
 
@@ -7736,16 +8067,23 @@ public:
 
    bool MenuWindowWindows(MenuItem selection, Modifiers mods)
    {
-      WindowList dialog { master = this };
-      Window document = (Window)dialog.Modal();
-      if(document)
+      WindowList
       {
-         if(activeChild.state == maximized)
-            document.SetState(maximized, false, mods);
-         else if(document.state == minimized)
-            document.SetState(normal, false, mods);
-         document.Activate();
-      }
+         master = this; isModal = true;
+
+         void NotifyDestroyed(Window window, DialogResult result)
+         {
+            Window document = (Window)(intptr)result;
+            if(document)
+            {
+               if(activeChild.state == maximized)
+                  document.SetState(maximized, false, 0);
+               else if(document.state == minimized)
+                  document.SetState(normal, false, 0);
+               document.Activate();
+            }
+         }
+      }.Create();
       return true;
    }
 
@@ -7784,12 +8122,13 @@ public:
    virtual bool OnMiddleButtonDown(int x, int y, Modifiers mods);
    virtual bool OnMiddleButtonUp(int x, int y, Modifiers mods);
    virtual bool OnMiddleDoubleClick(int x, int y, Modifiers mods);
+   virtual bool OnMultiTouch(TouchPointerEvent event, Array<TouchPointerInfo> infos, Modifiers mods);
    virtual void OnMouseCaptureLost(void);
    virtual void OnHScroll(ScrollBarAction action, int position, Key key);
    virtual void OnVScroll(ScrollBarAction action, int position, Key key);
    virtual void OnDrawOverChildren(Surface surface);
-   virtual bool OnFileModified(FileChange fileChange, char * param);
-   virtual bool OnSaveFile(char * fileName);
+   virtual bool OnFileModified(FileChange fileChange, const char * param);
+   virtual bool OnSaveFile(const char * fileName);
 
    // Virtual Methods -- Children management (To support Stacker, for lack of built-in auto-layout)
    // Note: A 'client' would refer to isActiveClient, rather than
@@ -7799,15 +8138,16 @@ public:
    virtual void OnChildResized(Window child, int x, int y, int w, int h);
 
    // Skins Virtual Functions
-   virtual void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h);
-   virtual void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh);
+   virtual void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h) { *w = 0, *h = 0; }
+   virtual void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh) { *mw = 0, *mh = 0; }
    virtual void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
    {
+      *x = 0, *y = 0;
       *cw = *w;
       *ch = *h;
    }
-   virtual void ShowDecorations(Font captionFont, Surface surface, char * name, bool active, bool moving);
-   virtual void PreShowDecorations(Font captionFont, Surface surface, char * name, bool active, bool moving);
+   virtual void ShowDecorations(Font captionFont, Surface surface, const char * name, bool active, bool moving);
+   virtual void PreShowDecorations(Font captionFont, Surface surface, const char * name, bool active, bool moving);
    virtual bool IsMouseMoving(int x, int y, int w, int h)
    {
       return false;
@@ -7830,7 +8170,7 @@ public:
    // Notifications
    virtual bool Window::NotifyActivate(Window window, bool active, Window previous);
    virtual void Window::NotifyDestroyed(Window window, DialogResult result);
-   virtual void Window::NotifySaved(Window window, char * filePath);
+   virtual void Window::NotifySaved(Window window, const char * filePath);
 
    // Public Methods
 
@@ -8018,7 +8358,7 @@ public:
                firewatchers font;
 
 
-               if(value.rootWindow && value.rootWindow.display && rootWindow)
+               if(value.rootWindow && value.rootWindow.display && rootWindow && created)
                {
                   bool reloadGraphics = (oldParent.rootWindow == oldParent && value.rootWindow) || (!value.rootWindow && rootWindow == this) ||
                         (value.rootWindow.display && value.rootWindow.display.displaySystem != rootWindow.display.displaySystem);
@@ -8105,7 +8445,7 @@ public:
       get { return master ? master : parent; }
    };
 
-   property char * caption
+   property const char * caption
    {
       property_category $"Appearance"
       watchable
@@ -8406,8 +8746,8 @@ public:
          {
             if(value)
             {
-               Window sibling;
-               /*for(sibling = parent.children.first; sibling; sibling = sibling.next)
+               /*Window sibling;
+               for(sibling = parent.children.first; sibling; sibling = sibling.next)
                   if(sibling != this && sibling.style.isDefault)
                      sibling.style.isDefault = false;*/
                if(master.defaultControl)
@@ -8676,11 +9016,6 @@ public:
       property_category $"Layout"
       isset
       {
-         Anchor thisAnchor = anchor;
-         SizeAnchor thisSizeAnchor = sizeAnchor;
-         bool leftRight = (anchor.left.type == none || anchor.left.type == middleRelative || anchor.right.type == none);
-         bool topBottom = (anchor.top.type == none || anchor.top.type == middleRelative || anchor.bottom.type == none);
-         bool isClient = !sizeAnchor.isClientW && !sizeAnchor.isClientH;
          return ((anchor.left.type == none || anchor.left.type == middleRelative || anchor.right.type == none) ||
                 (anchor.top.type == none || anchor.top.type == middleRelative || anchor.bottom.type == none)) &&
             !sizeAnchor.isClientW && !sizeAnchor.isClientH && sizeAnchor.size.w && sizeAnchor.size.h;
@@ -9046,10 +9381,14 @@ public:
                if(true || !parent.activeChild)
                   ActivateEx(true, false, true, true, null, null);
                */
-               if(creationActivation == activate)
+               if(creationActivation == activate && guiApp.desktop.active)
                   ActivateEx(true, false, true, true, null, null);
-               else if(creationActivation == flash && !object)
-                  Flash();
+               else if((creationActivation == activate || creationActivation == flash) && !object)
+               {
+                  MakeActive();
+                  if(this == rootWindow)
+                     Flash();
+               }
 
                //SetVisibility(!parent.style.hidden && (style.hidden ? false : true));
                Update(null);
@@ -9070,7 +9409,7 @@ public:
    property bool isDocument
    {
       property_category $"Document"
-      set { style.isDocument = value; }
+      set { style.isDocument = value; if(value) SetupFileMonitor(); }
       get { return style.isDocument; }
    };
 
@@ -9207,11 +9546,13 @@ public:
       get { return (bool)noAutoScrollArea; }
    };
 
-   property char * fileName
+   property const char * fileName
    {
       property_category $"Document"
       set
       {
+         SetupFileMonitor();
+
          if(menu && ((!fileName && value) || (fileName && !value)))
          {
             MenuItem item = menu.FindItem(MenuFileSave, 0);
@@ -9229,8 +9570,10 @@ public:
             UpdateCaption();
 
          // if(style.isDocument)
+#if !defined(__EMSCRIPTEN__)
          if(!saving)
             fileMonitor.fileName = value;
+#endif
       }
       get { return fileName; }
    };
@@ -9269,14 +9612,36 @@ public:
    property bool showInTaskBar
    {
       property_category $"Window Style"
-      set { style.showInTaskBar = value; }
-      get { return (style.showInTaskBar; }
+      set
+      {
+         style.showInTaskBar = value;
+#if defined(__WIN32__)
+         Win32UpdateStyle(this);
+#endif
+      }
+      get { return style.showInTaskBar; }
    };
    property FileDialog saveDialog { set { saveDialog = value; } };
    property bool isActiveClient
    {
       property_category $"Behavior"
-      set { style.isActiveClient = value; }
+      set
+      {
+         if(parent && style.isActiveClient != value && !style.hidden)
+         {
+            if(value)
+            {
+               if(state == minimized) parent.numIcons++;
+               parent.numPositions++;
+            }
+            else
+            {
+               if(state == minimized) parent.numIcons--;
+               parent.numPositions--;
+            }
+         }
+         style.isActiveClient = value;
+      }
       get { return style.isActiveClient; }
    };
 
@@ -9292,7 +9657,7 @@ public:
    };
 
 //#if !defined(ECERE_VANILLA)
-   property char * name
+   property const char * name
    {
       property_category $"Design"
       get
@@ -9306,7 +9671,7 @@ public:
       }
    };
 //#endif
-   property char * displayDriver
+   property const char * displayDriver
    {
       property_category $"Behavior"
       set
@@ -9352,7 +9717,7 @@ public:
    property Point clientStart { get { value = clientStart; } };
    property Point absPosition { get { value = absPosition; } };
    property Anchor normalAnchor { get { value = normalAnchor; } };
-   // property Size normalSizeAnchor { get { value = normalSizeAnchor; } };
+   property SizeAnchor normalSizeAnchor { get { value = normalSizeAnchor; } };
    property bool active { get { return (bool)active; } };
    property bool created { get { return (bool)created; } };
    property bool destroyed { get { return (bool)destroyed; } };
@@ -9392,8 +9757,38 @@ public:
       }
    };
    property bool moveable { get { return (bool)moveable; } set { moveable = value; } };
-   property bool alphaBlend { get { return (bool)alphaBlend; } set { alphaBlend = value; } };
+   property bool alphaBlend { get { return (bool)alphaBlend; } set { alphaBlend = value; if(value) nativeDecorations = false; /* Native Decorations are not supported with alphaBlend */ } };
    property bool useSharedMemory { get { return (bool)useSharedMemory; } set { useSharedMemory = value; } };
+   property GLCapabilities glCapabilities
+   {
+      get { return glCapabilities; }
+      set
+      {
+         bool reload = display != null &&
+            (glCapabilities.nonPow2Textures != value.nonPow2Textures ||
+             glCapabilities.intAndDouble != value.intAndDouble ||
+             glCapabilities.vertexBuffer != value.vertexBuffer ||
+             glCapabilities.compatible != value.compatible ||
+             glCapabilities.legacyFormats != value.legacyFormats ||
+             glCapabilities.debug != value.debug ||
+             glCapabilities.vertexPointer != value.vertexPointer ||
+             glCapabilities.quads != value.quads);
+         guiApp.modeSwitching = true;
+         if(reload)
+            UnloadGraphics(false);
+
+         glCapabilities = value;
+
+         if(reload)
+         {
+            if(SetupDisplay())
+               LoadGraphics(false, false);
+         }
+         else if(display)
+            display.glCapabilities = value;
+         guiApp.modeSwitching = false;
+      }
+   };
    property CreationActivationOption creationActivation { get { return creationActivation; } set { creationActivation = value; } };
    property bool nativeDecorations
    {
@@ -9425,7 +9820,7 @@ public:
    };
    property bool manageDisplay { get { return (bool)manageDisplay; } set { manageDisplay = value; } };
 
-   property char * text
+   property const char * text
    {
       property_category $"Deprecated"
       watchable
@@ -9522,23 +9917,15 @@ private:
    int numIcons;
    int positionID;
 
+#if !defined(__EMSCRIPTEN__)
    Mutex mutex;
+#endif
    WindowState lastState;
 
-   FileMonitor fileMonitor
-   {
-      this, FileChange { modified = true };
+#if !defined(__EMSCRIPTEN__)
+   FileMonitor fileMonitor;
+#endif
 
-      bool OnFileNotify(FileChange action, char * param)
-      {
-         incref this;
-         fileMonitor.StopMonitoring();
-         if(OnFileModified(action, param))
-            fileMonitor.StartMonitoring();
-         delete this;
-         return true;
-      }
-   };
    FontResource setFont, systemFont;
    FontResource usedFont;
    FontResource captionFont;
@@ -9554,6 +9941,8 @@ private:
    BitmapResource icon;
    void * windowData;
    CreationActivationOption creationActivation;
+   GLCapabilities glCapabilities;
+   glCapabilities = { true, true, true, true, true, true, true, true, true /*false*/, true, true, true, true, true, true, true };
    struct
    {
       bool active:1;            // true if window and ancestors are active
@@ -9587,6 +9976,7 @@ private:
       bool manageDisplay:1;
       bool formDesigner:1; // True if we this is running in the form editor
       bool requireRemaximize:1;
+      bool noConsequential:1;
    };
 
    // Checks used internally for them not to take effect in FormDesigner
@@ -9594,7 +9984,29 @@ private:
    property subclass(DisplayDriver) _displayDriver  { get { return !formDesigner ? dispDriver : null; } }
 
    WindowController controller;
-   public property WindowController controller { get { return controller; } set { delete controller; controller = value; if(controller) incref controller; } }
+
+   public property WindowController controller
+   {
+      get { return controller; }
+      set
+      {
+         if(controller)
+            controller.setWindow(null);
+         delete controller;
+         controller = value;
+         if(controller)
+         {
+            incref controller;
+            controller.setWindow(this);
+         }
+      }
+   }
+
+   public property bool noConsequential
+   {
+      set { noConsequential = value; }
+      get { return noConsequential; }
+   }
 };
 
 public class CommonControl : Window
@@ -9602,7 +10014,7 @@ public class CommonControl : Window
    // creationActivation = doNothing;
 
    ToolTip toolTip;
-   public property String toolTip
+   public property const String toolTip
    {
       property_category $"Appearance"
       set
@@ -9639,7 +10051,7 @@ public class CommonControl : Window
 
 public class Percentage : float
 {
-   char * OnGetString(char * string, float * fieldData, bool * needClass)
+   const char * OnGetString(char * string, float * fieldData, bool * needClass)
    {
       int c;
       int last = 0;
@@ -9662,7 +10074,7 @@ public class Percentage : float
    }
 };
 
-public void ApplySkin(Class c, char * name, void ** vTbl)
+public void ApplySkin(Class c, const char * name, void ** vTbl)
 {
    char className[1024];
    Class sc;
@@ -9703,10 +10115,7 @@ public void ApplySkin(Class c, char * name, void ** vTbl)
 
 public void UnapplySkin(Class c)
 {
-   char className[1024];
-   Class sc;
    subclass(Window) wc = (subclass(Window))c;
-   subclass(Window) base = (subclass(Window))c.base;
    OldLink d;
 
    if(wc.pureVTbl && c._vTbl != wc.pureVTbl)
@@ -9750,176 +10159,267 @@ class WindowControllerInterface : ControllableWindow
 {
    bool OnKeyDown(Key key, unichar ch)
    {
-      bool result = ((bool(*)(Window, WindowController, Key, unichar))(void *)controller.OnKeyDown)((Window)controller.controlled, controller, key, ch);
+      bool result = controller.OnKeyDown ? ((bool(*)(Window, WindowController, Key, unichar))(void *)controller.OnKeyDown)((Window)controller.controlled, controller, key, ch) : true;
       if(result)
-         result = ((bool (*)(Window, Key, unichar))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown])(controller.window, key, ch);
+      {
+         bool (* onKeyDown)(Window, Key, unichar) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown];
+         if(onKeyDown)
+            result = onKeyDown(controller.window, key, ch);
+      }
       return result;
    }
 
    bool OnKeyUp(Key key, unichar ch)
    {
-      bool result = ((bool(*)(Window, WindowController, Key, unichar))(void *)controller.OnKeyUp)((Window)controller.controlled, controller, key, ch);
+      bool result = controller.OnKeyUp ? ((bool(*)(Window, WindowController, Key, unichar))(void *)controller.OnKeyUp)((Window)controller.controlled, controller, key, ch) : true;
       if(result)
-         result = ((bool(*)(Window, Key, unichar))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp])(controller.window, key, ch);
+      {
+         bool (* onKeyUp)(Window, Key, unichar) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp];
+         if(onKeyUp)
+            result = onKeyUp(controller.window, key, ch);
+      }
       return result;
    }
 
    bool OnKeyHit(Key key, unichar ch)
    {
-      bool result = ((bool(*)(Window, WindowController, Key, unichar))(void *)controller.OnKeyHit)((Window)controller.controlled, controller, key, ch);
+      bool result = controller.OnKeyHit ? ((bool(*)(Window, WindowController, Key, unichar))(void *)controller.OnKeyHit)((Window)controller.controlled, controller, key, ch) : true;
       if(result)
-         result = ((bool(*)(Window, Key, unichar))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit])(controller.window, key, ch);
+      {
+         bool (* onKeyHit)(Window, Key, unichar) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit];
+         if(onKeyHit)
+            result = onKeyHit(controller.window, key, ch);
+      }
       return result;
    }
 
    bool OnMouseMove(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMouseMove)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnMouseMove ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMouseMove)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove])(controller.window, x, y, mods);
+      {
+         bool(* onMouseMove)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove];
+         if(onMouseMove)
+            result = onMouseMove(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnLeftButtonDown(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnLeftButtonDown)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnLeftButtonDown ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnLeftButtonDown)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown])(controller.window, x, y, mods);
+      {
+         bool(* onLeftButtonDown)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown];
+         if(onLeftButtonDown)
+            result = onLeftButtonDown(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnLeftButtonUp(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnLeftButtonUp)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnLeftButtonUp ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnLeftButtonUp)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp])(controller.window, x, y, mods);
+      {
+         bool(* onLeftButtonUp)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp];
+         if(onLeftButtonUp)
+            result = onLeftButtonUp(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnLeftDoubleClick)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnLeftDoubleClick ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnLeftDoubleClick)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick])(controller.window, x, y, mods);
+      {
+         bool(* onLeftDoubleClick)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick];
+         if(onLeftDoubleClick)
+            result = onLeftDoubleClick(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnRightButtonDown(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnRightButtonDown)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnRightButtonDown ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnRightButtonDown)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown])(controller.window, x, y, mods);
+      {
+         bool(* onRightButtonDown)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown];
+         if(onRightButtonDown)
+            result = onRightButtonDown(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnRightButtonUp(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnRightButtonUp)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnRightButtonUp ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnRightButtonUp)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp])(controller.window, x, y, mods);
+      {
+         bool(* onRightButtonUp)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp];
+         if(onRightButtonUp)
+            result = onRightButtonUp(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnRightDoubleClick(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnRightDoubleClick)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnRightDoubleClick ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnRightDoubleClick)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick])(controller.window, x, y, mods);
+      {
+         bool(* onRightDoubleClick)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick];
+         if(onRightDoubleClick)
+            result = onRightDoubleClick(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnMiddleButtonDown(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMiddleButtonDown)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnMiddleButtonDown ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMiddleButtonDown)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown])(controller.window, x, y, mods);
+      {
+         bool(* onMiddleButtonDown)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown];
+         if(onMiddleButtonDown)
+            result = onMiddleButtonDown(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnMiddleButtonUp(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMiddleButtonUp)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnMiddleButtonUp ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMiddleButtonUp)((Window)controller.controlled, controller, x, y, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp])(controller.window, x, y, mods);
+      {
+         bool(* onMiddleButtonUp)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp];
+         if(onMiddleButtonUp)
+            result = onMiddleButtonUp(controller.window, x, y, mods);
+      }
       return result;
    }
 
    bool OnMiddleDoubleClick(int x, int y, Modifiers mods)
    {
-      bool result = ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMiddleDoubleClick)((Window)controller.controlled, controller, x, y, mods);
+      bool result = controller.OnMiddleDoubleClick ? ((bool(*)(Window, WindowController, int, int, Modifiers))(void *)controller.OnMiddleDoubleClick)((Window)controller.controlled, controller, x, y, mods) : true;
+      if(result)
+      {
+         bool(* onMiddleDoubleClick)(Window, int, int, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick];
+         if(onMiddleDoubleClick)
+            onMiddleDoubleClick(controller.window, x, y, mods);
+      }
+      return result;
+   }
+
+   bool OnMultiTouch(TouchPointerEvent event, Array<TouchPointerInfo> infos, Modifiers mods)
+   {
+      bool result = controller.OnMultiTouch ? ((bool(*)(Window, WindowController, TouchPointerEvent event, Array<TouchPointerInfo> infos, Modifiers))(void *)controller.OnMultiTouch)((Window)controller.controlled, controller, event, infos, mods) : true;
       if(result)
-         result = ((bool(*)(Window, int, int, Modifiers))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick])(controller.window, x, y, mods);
+      {
+         bool(* onMultiTouch)(Window, TouchPointerEvent, Array<TouchPointerInfo>, Modifiers) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMultiTouch];
+         if(onMultiTouch)
+            onMultiTouch(controller.window, event, infos, mods);
+      }
       return result;
    }
 
    void OnResize(int width, int height)
    {
-      ((void(*)(Window, WindowController, int, int))(void *)controller.OnResize)((Window)controller.controlled, controller, width, height);
-      ((void(*)(Window, int, int))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnResize])(controller.window, width, height);
+      if(controller.OnResize)
+         ((void(*)(Window, WindowController, int, int))(void *)controller.OnResize)((Window)controller.controlled, controller, width, height);
+      {
+         void(* onResize)(Window, int, int) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnResize];
+         if(onResize)
+            onResize(controller.window, width, height);
+      }
    }
 
    void OnRedraw(Surface surface)
    {
-      ((void(*)(Window, WindowController, Surface))(void *)controller.OnRedraw)((Window)controller.controlled, controller, surface);
-      ((void(*)(Window, Surface))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw])(controller.window, surface);
+      if(controller.OnRedraw)
+         ((void(*)(Window, WindowController, Surface))(void *)controller.OnRedraw)((Window)controller.controlled, controller, surface);
+      {
+         void(* onRedraw)(Window, Surface) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw];
+         if(onRedraw)
+            onRedraw(controller.window, surface);
+      }
    }
 
    bool OnCreate()
    {
-      bool result = ((bool(*)(Window, WindowController))(void *)controller.OnCreate)((Window)controller.controlled, controller);
+      bool result = controller.OnCreate ? ((bool(*)(Window, WindowController))(void *)controller.OnCreate)((Window)controller.controlled, controller) : true;
       if(result)
-         result = ((bool(*)(Window))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnCreate])(controller.window);
+      {
+         bool(* onCreate)(Window) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnCreate];
+         if(onCreate)
+            result = onCreate(controller.window);
+      }
       return result;
    }
 
    bool OnLoadGraphics()
    {
-      bool result = ((bool(*)(Window, WindowController))(void *)controller.OnLoadGraphics)((Window)controller.controlled, controller);
+      bool result = controller.OnLoadGraphics ? ((bool(*)(Window, WindowController))(void *)controller.OnLoadGraphics)((Window)controller.controlled, controller) : true;
       if(result)
-         result = ((bool(*)(Window))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLoadGraphics])(controller.window);
+      {
+         bool(* onLoadGraphics)(Window) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLoadGraphics];
+         if(onLoadGraphics)
+            result = onLoadGraphics(controller.window);
+      }
       return result;
    }
 
    void OnUnloadGraphics()
    {
-      ((void(*)(Window, WindowController))(void *)controller.OnUnloadGraphics)((Window)controller.controlled, controller);
-      ((void(*)(Window))(void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnUnloadGraphics])(controller.window);
+      if(controller.OnUnloadGraphics)
+         ((void(*)(Window, WindowController))(void *)controller.OnUnloadGraphics)((Window)controller.controlled, controller);
+      {
+         void(* onUnloadGraphics)(Window) = (void *)controller.windowVTbl[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnUnloadGraphics];
+         if(onUnloadGraphics)
+            onUnloadGraphics(controller.window);
+      }
    }
 }
 
 public class WindowController<class V>
 {
-public:
-   property Window window
+   void setWindow(Window value)
    {
-      set
+      uint size = class(Window).vTblSize;
+      if(value)
       {
-         uint size = class(Window).vTblSize;
-         if(value)
+         delete windowVTbl;
+         windowVTbl = new void *[size];
+         memcpy(windowVTbl, value._vTbl, size * sizeof(void *));
+         if(value._vTbl == value._class._vTbl)
          {
-            windowVTbl = new void *[size];
-            memcpy(windowVTbl, value._vTbl, size * sizeof(void *));
-            if(value._vTbl == value._class._vTbl)
-            {
-               value._vTbl = new void *[value._class.vTblSize];
-               memcpy(value._vTbl + size, value._class._vTbl + size, (value._class.vTblSize - size) * sizeof(void *));
-            }
+            value._vTbl = new void *[value._class.vTblSize];
+            memcpy(value._vTbl + size, value._class._vTbl + size, (value._class.vTblSize - size) * sizeof(void *));
+         }
+         {
+            int c;
+            for(c = 0; c < size; c++)
             {
-               int c;
-               for(c = 0; c < size; c++)
-               {
-                  void * function = class(WindowControllerInterface)._vTbl[c];
-                  if(function != DefaultFunction)
-                     value._vTbl[c] = function;
-                  else
-                     value._vTbl[c] = windowVTbl[c];
-               }
+               void * function = class(WindowControllerInterface)._vTbl[c];
+               if(function && function != DefaultFunction)
+                  value._vTbl[c] = function;
+               else
+                  value._vTbl[c] = windowVTbl[c];
             }
          }
-         else
-            memcpy(value._vTbl, windowVTbl, class(Window).vTblSize * sizeof(void *));
-         window = value;
       }
+      else if(window)
+      {
+         memcpy(window._vTbl, windowVTbl, class(Window).vTblSize * sizeof(void *));
+         delete windowVTbl;
+      }
+      window = value;
+   }
+public:
+   property Window window
+   {
       get { return window; }
    }
    property V controlled
@@ -9927,6 +10427,7 @@ public:
       set { controlled = value; }
       get { return controlled; }
    }
+   // TODO: Add OnStateChange so we can implement SavedConfigWindow as a WindowController instead
    virtual bool V::OnKeyDown(WindowController controller, Key key, unichar ch);
    virtual bool V::OnKeyUp(WindowController controller, Key key, unichar ch);
    virtual bool V::OnKeyHit(WindowController controller, Key key, unichar ch);
@@ -9940,6 +10441,7 @@ public:
    virtual bool V::OnMiddleButtonDown(WindowController controller, int x, int y, Modifiers mods);
    virtual bool V::OnMiddleButtonUp(WindowController controller, int x, int y, Modifiers mods);
    virtual bool V::OnMiddleDoubleClick(WindowController controller, int x, int y, Modifiers mods);
+   virtual bool V::OnMultiTouch(WindowController controller, TouchPointerEvent event, Array<TouchPointerInfo> infos, Modifiers mods);
    virtual void V::OnResize(WindowController controller, int width, int height);
    virtual void V::OnRedraw(WindowController controller, Surface surface);
    virtual bool V::OnCreate(WindowController controller);
@@ -9947,7 +10449,7 @@ public:
    virtual void V::OnUnloadGraphics(WindowController controller);
 
 private:
-   int (** windowVTbl)();
+   public int (** windowVTbl)();
    V controlled;
    Window window;