autoLayout: Initial commit of AutoLayout and SlidesTest
authorJerome St-Louis <jerome@ecere.com>
Wed, 1 Jun 2016 03:30:41 +0000 (23:30 -0400)
committerJerome St-Louis <jerome@ecere.com>
Mon, 21 Nov 2016 14:18:51 +0000 (09:18 -0500)
14 files changed:
autoLayout/SlideTest.epj [new file with mode: 0644]
autoLayout/autoLayout/autoLayout.ec [new file with mode: 0644]
autoLayout/autoLayout/wrapText.ec [new file with mode: 0644]
autoLayout/base.ec [new file with mode: 0644]
autoLayout/graphics/bg1.png [new file with mode: 0644]
autoLayout/slides.ec [new file with mode: 0644]
autoLayout/slides/buttonSlide.ec [new file with mode: 0644]
autoLayout/slides/classesSlide.ec [new file with mode: 0644]
autoLayout/slides/formSlide.ec [new file with mode: 0644]
autoLayout/slides/gnosisSlide.ec [new file with mode: 0644]
autoLayout/slides/instancesSlide.ec [new file with mode: 0644]
autoLayout/slides/languageSlide.ec [new file with mode: 0644]
autoLayout/slides/slide1.ec [new file with mode: 0644]
autoLayout/slides/titleSlide.ec [new file with mode: 0644]

diff --git a/autoLayout/SlideTest.epj b/autoLayout/SlideTest.epj
new file mode 100644 (file)
index 0000000..d454526
--- /dev/null
@@ -0,0 +1,71 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "SlideTest",
+   "Options" : {
+      "Warnings" : "All",
+      "PreprocessorDefinitions" : [
+         "IMPORT_STATIC=\"\""
+      ],
+      "TargetType" : "Executable",
+      "TargetFileName" : "SlideTest",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Optimization" : "None",
+            "PreprocessorDefinitions" : [
+               "_DEBUG"
+            ],
+            "Console" : true,
+            "FastMath" : false
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Debug" : false,
+            "Optimization" : "Speed",
+            "FastMath" : true
+         }
+      }
+   ],
+   "Files" : [
+      {
+         "Folder" : "autoLayout",
+         "Files" : [
+            "autoLayout.ec",
+            "wrapText.ec"
+         ]
+      },
+      {
+         "Folder" : "graphics",
+         "Files" : [
+            "bg1.png"
+         ]
+      },
+      {
+         "Folder" : "slides",
+         "Files" : [
+            "slide1.ec",
+            "./base.ec",
+            "titleSlide.ec",
+            "languageSlide.ec",
+            "classesSlide.ec",
+            "instancesSlide.ec",
+            "formSlide.ec",
+            "buttonSlide.ec",
+            "gnosisSlide.ec"
+         ]
+      },
+      "slides.ec"
+   ],
+   "ResourcesPath" : "",
+   "Resources" : [
+
+   ]
+}
diff --git a/autoLayout/autoLayout/autoLayout.ec b/autoLayout/autoLayout/autoLayout.ec
new file mode 100644 (file)
index 0000000..894e795
--- /dev/null
@@ -0,0 +1,561 @@
+import "ecere"
+import "wrapText"
+
+// Add 'unset' ?
+enum UnitType { percent, pixels, points };
+
+struct Dimension
+{
+   UnitType type;
+   union { double d; int i; };
+   property double { set { d = value; type = percent; } get { return 0; }  /* TODO: Compute this? Avoid use? */ }
+   property int    { set { i = value; type = pixels; } get { return 0; } /* TODO: Compute this? Avoid use? */ }
+   int getPixels(int p) { return (type == pixels) ? i : (int)(d * p + 0.5); }
+};
+
+struct DimensionBox { Dimension left, top, right, bottom; };
+
+struct Dimensions { Dimension w, h; };
+
+enum Direction { horizontal, vertical };
+
+enum HVAlignment : Alignment { bottom = right, top = left };
+enum SelfAlignment : HVAlignment { inherit };
+
+FontResource defaultFont { "Tahoma", 8.25f };
+
+class Element
+{
+private:
+   List<Element> nodes;
+   DimensionBox margin;
+   DimensionBox border;
+   DimensionBox padding;
+   Size contentSize; // Computed from the content (Max of Text/Primitives extent and Children minimum extent)
+   Element parent;
+
+   Dimensions minSize, maxSize;  // Size specification
+   Size clientSize;
+   BorderStyle borderStyle;
+   Point scroll;
+   bool hScroll, vScroll;
+   const String caption;
+   ColorAlpha bgColor;
+   ColorAlpha fgColor;
+   ColorAlpha borderColor;
+   Direction direction;
+   bool autoLayoutFlag;
+   Point position; // Position relative to parent's client area, including margin offset
+   Point tlPosition;
+   HVAlignment hAlignment, vAlignment; // Alignment of content (children or graphics) ?
+   SelfAlignment selfHAlignment, selfVAlignment;
+
+   selfHAlignment = inherit;
+   selfVAlignment = inherit;
+
+   autoLayoutFlag = true;
+   fgColor = black;
+   bgColor = 0;
+
+   FontResource font;
+   Font fontObject;
+   BitmapResource bitmap;
+   Bitmap bmpObject;
+   ColorAlpha bitmapTint;
+   bitmapTint = white;
+   /*
+   List<Primitive> primitives;   // Displays above nodes
+   List<Primitive> bgPrimitives; // Displays behind nodes, need to figure out how to scale to width (e.g. Rectangle with a gradient)
+   List<Effects> effects;
+   */
+
+   void loadGraphics(DisplaySystem displaySystem)
+   {
+      if(!fontObject)
+      {
+         Element e = this;
+         FontResource font;
+         while(e && !e.font) e = e.parent;
+         font = e ? e.font : defaultFont;
+         displaySystem.LoadResource(font);
+         fontObject = font.font;
+      }
+
+      if(!bmpObject && bitmap)
+      {
+         displaySystem.LoadResource(bitmap);
+         bmpObject = bitmap.bitmap;
+      }
+
+      for(n : nodes)
+         n.loadGraphics(displaySystem);
+   }
+
+   void computeContentSize(DisplaySystem displaySystem)
+   {
+      int cw = clientSize.w, ch = clientSize.h;
+      int minimum = 0, thickness = 0;
+      Size graphicsSize { };
+      int rcw = cw, rch = ch;
+      for(n : nodes)
+      {
+         Element e = n;
+         if(e.autoLayoutFlag)
+         {
+            int xw = e.maxSize.w.getPixels(cw);
+            int xh = e.maxSize.h.getPixels(ch);
+            int nw = e.minSize.w.getPixels(cw);
+            int nh = e.minSize.h.getPixels(ch);
+            int w = 0, h = 0;
+            Box m
+            {
+               left = e.margin.left.getPixels(cw);
+               right = e.margin.right.getPixels(cw);
+               top = e.margin.top.getPixels(ch);
+               bottom = e.margin.bottom.getPixels(ch);
+            };
+
+            xw += m.left = m.right;
+            xh += m.top = m.bottom;
+            nw += m.left = m.right;
+            nh += m.top = m.bottom;
+
+            if(xw > w)
+               w = Min(xw, rcw);
+            //if(xh > h)
+               h = Min(xh, rch);
+
+            if(xw && xw < w) w = xw;
+            if(xh && xh < h) h = xh;
+            if(nw && nw > w) w = nw;
+            if(nh && nh > h) h = nh;
+
+            if(!w) w = cw;
+            if(!h) h = ch;
+
+            w -= m.left + m.right;
+            h -= m.top + m.bottom;
+
+            e.clientSize = { w, h };
+
+            e.computeContentSize(displaySystem);
+            if(!e.hScroll && e.contentSize.w > nw) nw = e.contentSize.w;
+            if(!e.vScroll && e.contentSize.h > nh) nh = e.contentSize.h;
+
+            if(direction == horizontal)
+               rcw -= nw;
+
+            if(direction == horizontal)
+            {
+               minimum += nw;
+               if(nh > thickness) thickness = nh;
+            }
+            else
+            {
+               minimum += nh;
+               if(nw > thickness) thickness = nw;
+            }
+         }
+      }
+      if(caption)
+      {
+         /*if(!cw) cw = MAXINT;
+         if(!ch) ch = MAXINT;*/
+
+         //displaySystem.FontExtent(fontObject, caption, strlen(caption), (int *)&graphicsSize.w, (int *)&graphicsSize.h);
+         wrapTextExtent(displaySystem, fontObject, caption, cw, ch, (int *)&graphicsSize.w, (int *)&graphicsSize.h);
+         if(graphicsSize.w)
+            graphicsSize.w += 1;
+      }
+
+      // Set the content size to the max of (minimum extent of children, graphics extent)
+      if(direction == horizontal)
+         contentSize = { Max(graphicsSize.w, minimum), Max(graphicsSize.h, thickness) };
+      else
+         contentSize = { Max(graphicsSize.w, thickness), Max(graphicsSize.h, minimum) };
+   }
+
+   void updateTLPosition()
+   {
+      for(n : nodes)
+      {
+         Element e = n;
+         e.tlPosition = { e.position.x + tlPosition.x, e.position.y + tlPosition.y };
+         n.updateTLPosition();
+      }
+   }
+
+   void autoLayout()
+   {
+      int cw = clientSize.w, ch = clientSize.h;
+      int totalMax = 0;
+      int totalUsed[Alignment] = { 0 };
+      int thickness = direction == vertical   ? clientSize.w : clientSize.h;
+      int start = 0;
+      SelfAlignment lastAlignment = (direction == horizontal) ? nodes[0].selfHAlignment : nodes[0].selfVAlignment;
+      int totalMin = 0;
+      if(lastAlignment == inherit) lastAlignment = (direction == horizontal) ? hAlignment : vAlignment;
+
+      // Allocate extra space
+      for(n : nodes)
+      {
+         Element e = n;
+         e.clientSize = { };
+         if(e.autoLayoutFlag)
+         {
+            Box m
+            {
+               left = e.margin.left.getPixels(cw);
+               right = e.margin.right.getPixels(cw);
+               top = e.margin.top.getPixels(ch);
+               bottom = e.margin.bottom.getPixels(ch);
+            };
+
+            if(direction == horizontal)
+            {
+               int xw = e.maxSize.w.getPixels(cw);
+               int nw = e.minSize.w.getPixels(cw);
+               int w = Max(nw, e.contentSize.w);
+               int mm = m.left + m.right;
+               if(xw && xw < w) w = xw;
+
+               totalMin += w + mm;
+
+               if(xw && w > xw) xw = w;
+
+               totalMax += Max(w, xw) + mm;
+               totalUsed[e.selfHAlignment == inherit ? hAlignment : e.selfHAlignment] += Max(xw, w) + mm;
+            }
+            else
+            {
+               int xh = e.maxSize.h.getPixels(ch);
+               int nh = e.minSize.h.getPixels(ch);
+               int h = Max(nh, e.contentSize.h);
+               int mm = m.top + m.bottom;
+               if(xh && xh < h) h = xh;
+
+               totalMin += h + mm;
+
+               if(xh && h > xh) xh = h;
+
+               totalMax += Max(h, xh) + mm;
+               totalUsed[e.selfVAlignment == inherit ? vAlignment : e.selfVAlignment] += Max(xh, h) + mm;
+            }
+         }
+      }
+
+      if(direction == horizontal)
+      {
+         if(totalUsed[lastAlignment] < cw)
+         {
+            if(lastAlignment == right)
+               start = (cw - totalUsed[lastAlignment]);
+            else if(lastAlignment == center)
+               start = (cw - totalUsed[lastAlignment]) / 2;
+         }
+      }
+      else
+      {
+         if(totalUsed[lastAlignment] < ch)
+         {
+            if(lastAlignment == right)
+               start = (ch - totalUsed[lastAlignment]);
+            else if(lastAlignment == center)
+               start = (ch - totalUsed[lastAlignment]) / 2;
+         }
+      }
+
+      for(n : nodes)
+      {
+         Element e = n;
+         int w = e.hScroll ? 0 : e.contentSize.w;
+         int h = e.vScroll ? 0 : e.contentSize.h;
+         int nw = e.minSize.w.getPixels(cw);
+         int nh = e.minSize.h.getPixels(ch);
+         int xw = e.maxSize.w.getPixels(cw);
+         int xh = e.maxSize.h.getPixels(ch);
+         Box m
+         {
+            left = e.margin.left.getPixels(cw);
+            right = e.margin.right.getPixels(cw);
+            top = e.margin.top.getPixels(ch);
+            bottom = e.margin.bottom.getPixels(ch);
+         };
+         bool positionUpdated = false;
+
+         if(nw > w) w = nw;
+         if(nh > h) h = nh;
+
+         w += m.left + m.right;
+         h += m.top + m.bottom;
+         xw += m.left + m.right;
+         xh += m.top + m.bottom;
+
+         if(xw && w > xw) w = xw;
+         if(xh && h > xh) h = xh;
+
+         if(direction == horizontal)
+         {
+            SelfAlignment alignment = e.selfHAlignment == inherit ? hAlignment : e.selfHAlignment;
+            int y = 0;
+            xw = (w < xw && totalMax > totalMin && cw > totalMin) ? (int)(w + (xw - w) * Min(1.0f, (float)(cw - totalMin) / (totalMax - totalMin))) : 0;
+
+            if(xw > w) w = xw;
+            if(xh > h) h = xh;
+            if(h > thickness) h = thickness;
+
+            w = Min(w, Max(0, cw - start));
+
+            if(alignment != lastAlignment && totalUsed[alignment] < cw && lastAlignment != right && (lastAlignment != center || alignment == right))
+            {
+               int newStart = (cw - totalUsed[alignment]);
+               if(alignment == center)
+                  newStart /= 2;
+               if(newStart > start)
+                  start = newStart;
+               lastAlignment = alignment;
+            }
+
+            switch(e.selfVAlignment == inherit ? vAlignment : e.selfVAlignment)
+            {
+               case right: y = thickness - h; break;
+               case center: y = (thickness - h) / 2; break;
+            }
+            e.position = { start, y + m.top };
+            start += w;
+         }
+         else
+         {
+            SelfAlignment alignment = e.selfVAlignment == inherit ? vAlignment : e.selfVAlignment;
+            int x = 0;
+            xh = (h < xh && totalMax > totalMin && ch > totalMin) ? (int)(h + (xh - h) * Min(1.0f, (float)(ch - totalMin) / (totalMax - totalMin))) : 0;
+
+            if(xh > h) h = xh;
+            if(xw > w) w = xw;
+            if(w > thickness) w = thickness;
+
+            h = Min(h, Max(0, ch - start));
+
+            if(alignment != lastAlignment && totalUsed[alignment] < ch && lastAlignment != right && (lastAlignment != center || alignment == right))
+            {
+               int newStart = (ch - totalUsed[alignment]);
+               if(alignment == center)
+                  newStart /= 2;
+               if(newStart > start)
+                  start = newStart;
+               lastAlignment = alignment;
+            }
+
+            switch(e.selfHAlignment == inherit ? hAlignment : e.selfHAlignment)
+            {
+               case right: x = thickness - w; break;
+               case center: x = (thickness - w) / 2; break;
+            }
+
+            e.position = { x + m.left, start };
+            start += h;
+         }
+         w -= m.left + m.right;
+         h -= m.top + m.bottom;
+
+         if(w != e.clientSize.w || h != e.clientSize.h)
+         {
+            e.tlPosition = { e.position.x + tlPosition.x, e.position.y + tlPosition.y };
+            e.clientSize = { w, h };
+            if(e.autoLayoutFlag && e.nodes)
+               e.autoLayout();
+         }
+         else
+         {
+            if(e.tlPosition.x != e.position.x + tlPosition.x || e.tlPosition.y != e.position.y + tlPosition.y )
+               positionUpdated = true;
+            e.tlPosition = { e.position.x + tlPosition.x, e.position.y + tlPosition.y };
+            if(positionUpdated && e.nodes)
+               e.updateTLPosition();
+         }
+      }
+   }
+
+   void render(Surface surface)
+   {
+      surface.background = bgColor;
+      surface.Area(tlPosition.x, tlPosition.y, tlPosition.x + clientSize.w - 1, tlPosition.y + clientSize.h - 1);
+      if(bmpObject)
+      {
+         int sw = bmpObject.width, sh = bmpObject.height;
+         int x = (clientSize.w - sw) / 2;
+         int y = (clientSize.h - sh) / 2;
+         surface.blitTint = bitmapTint;
+         surface.Blit(bmpObject, x,y,0,0, sw,sh);
+      }
+      if(caption)
+      {
+         int sw = contentSize.w, sh = contentSize.h;
+         int x = tlPosition.x, y = tlPosition.y;
+         if(hAlignment == center)
+            x += (clientSize.w - sw) / 2;
+         if(vAlignment == center)
+            y += (clientSize.h - sh) / 2;
+
+         surface.foreground = fgColor;
+         surface.font = fontObject;
+         // surface.WriteText(x, y, caption, strlen(caption));
+         wrapText(surface, caption, x, y, tlPosition.x + clientSize.w, tlPosition.y + clientSize.h);
+      }
+      if(nodes)
+      {
+         for(n : nodes)
+            n.render(surface);
+      }
+   }
+
+public:
+   property Element parent
+   {
+      set
+      {
+         if(!value.nodes) value.nodes = { };
+         value.nodes.Add(this);
+
+         parent = value;
+      }
+   }
+}
+
+/*
+class Elemental : Col
+{
+   Bar r1
+   {
+      Element b0 { caption = "<<" };
+      Bar s1 { };
+      Element b1 { caption = "The" };
+      Element b2 { caption = "Quick" };
+      Element b3 { caption = "Brown" };
+      Bar s2 { };
+   };
+   Bar r2
+   {
+      Element b4 { caption = "Fox." };
+      Element b5 { };
+      Element b6 { };
+   };
+   Bar r3
+   {
+      Element b7 { caption = "Left" };
+      Element b8 { caption = "Address Bar" };
+      Element b9 { caption = "Right" };
+   };
+}
+
+{ [
+   { "class == Elemental", bgColor = ivory },
+   { "id == Elemental::r1", bgColor = gray, maxSize = { 100%, 100 } },
+   { "id == Elemental::b0", fgColor = white, bgColor = navy },
+   { "id == Elemental::b1", bgColor = red },
+   { "id == Elemental::b2", bgColor = blue, fgColor = white },
+
+   { "id == Elemental::r2", bgColor = lightGray, maxSize = { 100%, 150 } },
+   { "id == Elemental::b4", bgColor = yellow },
+   { "id == Elemental::b5", bgColor = aquamarine, maxSize = { 25%, 50 } },
+   { "id == Elemental::b6", bgColor = tomato, maxSize = { 50%, 50 } },
+
+   { "id == Elemental::r3", bgColor = lightGray, maxSize = { 100%, 0 } },
+   { "id == Elemental::b7", bgColor = skyBlue },
+   { "id == Elemental::b8", bgColor = teal, maxSize.w = 100% },
+   { "id == Elemental::b9", bgColor = maroon }
+] };
+*/
+
+class Bar : Element
+{
+   direction = horizontal;
+   maxSize = { 1.0, 1.0 };
+}
+
+class Col : Element
+{
+   direction = vertical;
+   maxSize = { 1.0, 1.0 };
+}
+
+/*
+class Elemental : Col
+{
+   bgColor = ivory;
+
+   Bar header { this, bgColor = blue };
+   Bar middle { this, bgColor = white };
+      Col col1 { middle, bgColor = lime };
+         Element e1 { col1, caption = "Foo", bgColor = gray, selfHAlignment = center, selfVAlignment = center };
+      Col col2 { middle, bgColor = skyBlue };
+         Element e2 { col2, caption = "Bar", bgColor = lightGray, selfHAlignment = center, selfVAlignment = center };
+      Col col3 { middle, bgColor = tomato };
+         Element e3 { col3, caption = "Third", bgColor = lightGray, selfHAlignment = center, selfVAlignment = center };
+   Bar footer { this, bgColor = red };
+}
+
+class Elemental2 : Col
+{
+   bgColor = skyBlue;
+
+   Bar r1 { this };
+      Col c1 { r1, maxSize.w = 0.25, bgColor = blue };
+      Col c2 { r1, maxSize.w = 0.5, bgColor = red };
+      Col c3 { r1, maxSize.w = 0.25, bgColor = blue };
+
+   Bar r { this, bgColor = beige, maxSize.h = 10 };
+
+   Bar r2 { this };
+      Col d1 { r2, maxSize.w = 0.25, bgColor = blue };
+      Col d11 { r2, maxSize.w = 0.5, caption = "Hello", bgColor = green };
+      Col d2 { r2, maxSize.w = 0.5, bgColor = red };
+      Col d3 { r2, maxSize.w = 0.25, bgColor = blue };
+      Col d4 { r2, maxSize.w = 0.25, bgColor = green };
+
+   Bar rr { this, bgColor = beige, maxSize.h = 10 };
+
+   Bar r3 { this };
+      Col { r3, minSize.w = 30, maxSize.w = 0,  bgColor = blue };
+      Col { r3, maxSize.w = 1.0, caption = "Hello", bgColor = green };
+      Col { r3, minSize.w = 30, maxSize.w = 0, bgColor = red };
+}
+*/
+
+class AutoLayoutForm : Window
+{
+   displayDriver = "OpenGL";
+   caption = "";
+   background = formColor;
+   borderStyle = sizable;
+   hasMaximize = true;
+   hasMinimize = true;
+   hasClose = true;
+   clientSize = { 640, 480 };
+
+   Element contents;
+
+   bool OnLoadGraphics()
+   {
+      contents.loadGraphics(displaySystem);
+      return true;
+   }
+
+   void OnResize(int width, int height)
+   {
+      int nw = contents.minSize.w.getPixels(width);
+      int nh = contents.minSize.h.getPixels(height);
+      contents.clientSize = { Max(nw, width), Max(nh, height) };
+      if(contents.nodes)
+      {
+         contents.computeContentSize(displaySystem);
+         contents.autoLayout();
+      }
+      Update(null);
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      contents.render(surface);
+   }
+}
diff --git a/autoLayout/autoLayout/wrapText.ec b/autoLayout/autoLayout/wrapText.ec
new file mode 100644 (file)
index 0000000..7cdb3e3
--- /dev/null
@@ -0,0 +1,135 @@
+import IMPORT_STATIC "ecere"
+
+int wrapText(Surface surface, const String text, int sx, int sy, int ex, int ey)
+{
+   const String start = text;
+   const String drawUntil = null;
+   int w = 0;
+   int y = sy;
+   int tw, th;
+   int lh;
+
+   surface.TextExtent("W", 1, &tw, &th);
+   lh = th;
+   surface.Clip({ sx, sy, ex, ey });
+
+   while(true)
+   {
+      bool canAddMore = false;
+      if(ey - y >= 2*th)
+      {
+         const String s = drawUntil ? drawUntil + 1 : start;
+         const String nextSpace = strchr(s, ' ');
+         const String newLine = strchr(s, '\n');
+         if(newLine && (!nextSpace || (newLine < nextSpace)))
+            nextSpace = newLine;
+         if(!nextSpace)
+            nextSpace = strchr(s, 0);
+         if(nextSpace)
+         {
+            surface.TextExtent(drawUntil ? drawUntil : start, (int)(nextSpace - (drawUntil ? drawUntil : start)), &tw, &th);
+            if(!th) th = lh;
+            if(w + tw < ex - sx || !drawUntil)
+            {
+               drawUntil = nextSpace;
+               w += tw;
+               if(*nextSpace != 0 && *nextSpace != '\n')
+                  canAddMore = true;
+            }
+         }
+      }
+      if(!canAddMore)
+      {
+         if(drawUntil)
+         {
+            surface.WriteText(sx, y, start, (int)(drawUntil - start));
+            w = 0;
+            y += th;
+            if(!*drawUntil)
+               break;
+            start = drawUntil + 1;
+            drawUntil = null;
+         }
+         else
+         {
+            surface.WriteText(sx, y, start, strlen(start));
+            y += th;
+            break;
+         }
+      }
+   }
+
+   surface.Clip(null);
+   return y;
+}
+
+int wrapTextExtent(DisplaySystem displaySystem, Font font, const String text, int ex, int ey, int * wtw, int * wth)
+{
+   const String start = text;
+   const String drawUntil = null;
+   int w = 0;
+   int y = 0;
+   int tw, th;
+   int lh;
+
+   displaySystem.FontExtent(font, "W", 1, &tw, &th);
+   lh = th;
+
+   *wtw = 0;
+   *wth = th;
+
+   while(true)
+   {
+      bool canAddMore = false;
+      if(ey - y >= 2*th)
+      {
+         const String s = drawUntil ? drawUntil + 1 : start;
+         const String nextSpace = strchr(s, ' ');
+         const String newLine = strchr(s, '\n');
+         if(newLine && (!nextSpace || (newLine < nextSpace)))
+            nextSpace = newLine;
+         if(!nextSpace)
+            nextSpace = strchr(s, 0);
+         if(nextSpace)
+         {
+            displaySystem.FontExtent(font, drawUntil ? drawUntil : start, (int)(nextSpace - (drawUntil ? drawUntil : start)), &tw, &th);
+            if(!th) th = lh;
+            if(w + tw < ex || !drawUntil)
+            {
+               drawUntil = nextSpace;
+               w += tw;
+               if(*nextSpace != 0 && *nextSpace != '\n')
+                  canAddMore = true;
+            }
+         }
+      }
+      if(!canAddMore)
+      {
+         if(!drawUntil && !y && !w)
+         {
+            drawUntil = strchr(start, '\0');
+            if(drawUntil)
+               displaySystem.FontExtent(font, start, (int)(drawUntil - start), &w, &th);
+         }
+         if(drawUntil)
+         {
+            *wtw = Max(*wtw, w);
+            *wth = Max(*wth, y + th);
+            w = 0;
+            y += th;
+            if(!*drawUntil)
+               break;
+            start = drawUntil + 1;
+            drawUntil = null;
+         }
+         else
+         {
+            *wtw = Max(*wtw, w);
+            *wth = Max(*wth, y + th);
+            y += th;
+            break;
+         }
+      }
+   }
+   return y;
+}
diff --git a/autoLayout/base.ec b/autoLayout/base.ec
new file mode 100644 (file)
index 0000000..101f143
--- /dev/null
@@ -0,0 +1,46 @@
+import "autoLayout"
+
+class Title : Element { maxSize = { 1430, 0 }, font = { "Verdana", 60, bold = true }, fgColor = textColor; };
+class CCol  : Col { hAlignment = center, vAlignment = center; };
+class BMBar : Bar { maxSize.h = 60; };
+class MBar  : Bar { maxSize.h = 30; };
+define textColor = 0xFF2F3A3E;
+class Header : Element { font = { "Verdana", 50, bold = true }; fgColor = textColor; }
+class SmallHeader : Element { font = { "Verdana", 30, bold = true }; fgColor = textColor; }
+class Bullet : Element
+{
+   fgColor = textColor;
+   property const String caption
+   {
+      set { text.caption = value; }
+      get { return text.caption; }
+   }
+   Element bullet { this, caption = "▪ ", fgColor = 0xFF104A4A; };
+   Col text { this, fgColor = textColor; };
+}
+
+class BaseSlide : CCol
+{
+   bitmap = { "graphics/bg1.png" };
+   bitmapTint = { 41, white };
+   bgColor = 0x59C6D2E3;
+   font = { "Verdana", 32 };
+}
+
+class CodeBlock : CCol
+{
+   selfHAlignment = center, selfVAlignment = center;
+   vAlignment = center;
+   maxSize = { };
+   //bgColor = { 180, 0x094B55 };
+   bgColor = { 220, black };
+   font = { "Consolas", 30 };
+
+   // Since we don't have padding yet
+   CCol text { this, fgColor = lime /*white*/; maxSize = { 1.0, 1.0 }; /*margin = { 20, 20, 20, 20 }*/ };
+   property const String caption
+   {
+      set { text.caption = value; }
+      get { return text.caption; }
+   }
+}
diff --git a/autoLayout/graphics/bg1.png b/autoLayout/graphics/bg1.png
new file mode 100644 (file)
index 0000000..c6167e5
Binary files /dev/null and b/autoLayout/graphics/bg1.png differ
diff --git a/autoLayout/slides.ec b/autoLayout/slides.ec
new file mode 100644 (file)
index 0000000..f00ec77
--- /dev/null
@@ -0,0 +1,100 @@
+import "autoLayout"
+
+import "titleSlide"
+import "languageSlide"
+import "classesSlide"
+import "instancesSlide"
+import "formSlide"
+import "buttonSlide"
+import "gnosisSlide"
+
+Array<Class> slides
+{ [
+   class(TitleSlide),
+   class(LanguageSlide),
+   class(ClassesSlide),
+   class(InstancesSlide),
+   class(FormSlide),
+   class(ButtonSlide),
+   class(GNOSISSlide)
+] };
+
+class SlideForm : AutoLayoutForm
+{
+   int slideNum;
+   caption = "Butterbur Slides";
+
+   SlideForm()
+   {
+      subclass(Element) c = slides.count ? (subclass(Element))slides[slideNum] : null;
+      if(c)
+         contents = eInstance_New(c);
+   }
+
+   bool OnKeyHit(Key key, unichar ch)
+   {
+      int num = slideNum;
+      switch(key)
+      {
+         case home:     num = 0; break;
+         case end:      if(slides.count) num = slides.count-1; break;
+         case pageUp:   if(num > 0) num--; break;
+         case pageDown: if(num < slides.count-1) num++; break;
+         case p:        printSlides(caption); break;
+      }
+      if(slideNum != num)
+      {
+         delete contents;
+         slideNum = num;
+         contents = eInstance_New(slides[num]);
+         OnLoadGraphics();
+         OnResize(clientSize.w, clientSize.h);
+      }
+      return true;
+   }
+}
+
+SlideForm slideForm { clientSize = { 1600, 1200 } };
+
+class SlidePrinter : Window
+{
+   fullRender = true;
+   size = { 1600, 1200 };
+   displayDriver = "Win32Printer";
+}
+
+Bitmap outputSlide(Class sc)
+{
+   Element c = eInstance_New(sc);
+   Bitmap bmp { };
+   AutoLayoutForm form1 { contents = c, clientSize = { 1600, 1200 } };
+   bmp.Allocate(null, 1600, 1200, 0, pixelFormat888, false);
+   form1.Create();
+   form1.display.Lock(true);
+   form1.Grab(bmp, null, false);
+   form1.display.Unlock();
+   form1.Destroy(0);
+   delete c;
+   bmp.Save("test.png", null, null);
+   return bmp;
+}
+
+void printSlides(const String title)
+{
+   int i = 0;
+   SlidePrinter printer { size = { 1600, 1200 } };
+   SetPrintingDocumentName(title);
+
+   printer.Create();
+   for(s : slides)
+   {
+      Bitmap bmp = outputSlide(s);
+      Picture pic { printer, anchor = { 0, 0, 0, 0 }, bitmapImage = bmp };
+      if(i++) printer.display.NextPage();
+      pic.Create();
+      pic.UpdateDisplay();
+      pic.Destroy(0);
+      delete bmp;
+   }
+   printer.Destroy(0);
+}
diff --git a/autoLayout/slides/buttonSlide.ec b/autoLayout/slides/buttonSlide.ec
new file mode 100644 (file)
index 0000000..de65666
--- /dev/null
@@ -0,0 +1,31 @@
+import "base"
+
+class ButtonSlide : BaseSlide
+{
+   BMBar { this };
+   Title title { this, caption = "A Button instance" };
+
+   MBar { this };
+   Col t { this, maxSize = { 1550, 1250 }, margin = { 60, 15, 60, 15 } };
+      //Header { t, caption = "Features" };
+      MBar { t };
+      Bullet { t, caption = "Form controls defined as member instances" };
+      Bullet { t, caption = "Events defined by overriding methods" };
+      BMBar { t };
+      SmallHeader { t, caption = "                Syntax:" };
+      MBar { t };
+      CodeBlock { t, minSize = { 1200, 600 }, caption =
+         "Button button1\n"
+         "{\n"
+         "   this, caption = $\"button1\",\n"
+         "   position = { 272, 248 };\n"
+         "\n"
+         "   bool NotifyClicked(Button button,\n"
+         "      int x, int y, Modifiers mods)\n"
+         "   {\n"
+         "\n"
+         "      return true;\n"
+         "   }\n"
+         "};\n"
+      };
+}
diff --git a/autoLayout/slides/classesSlide.ec b/autoLayout/slides/classesSlide.ec
new file mode 100644 (file)
index 0000000..f1774a4
--- /dev/null
@@ -0,0 +1,31 @@
+import "base"
+
+class ClassesSlide : BaseSlide
+{
+   BMBar { this };
+   Title title { this, caption = "Classes" };
+
+   MBar { this };
+   //Col t { this, maxSize = { 1450, 1150 }, margin = { 115, 15, 115, 15 } };
+   Col t { this, maxSize = { 1550, 1250 }, margin = { 60, 15, 60, 15 } };
+      //Header { t, caption = "Features" };
+      BMBar { t };
+      Bullet { t, caption = "Define a 'class' of objects for reusability" };
+      Bullet { t, caption = "Inheritance to define specialized classes" };
+      Bullet { t, caption = "Can contain methods, properties and data members" };
+      BMBar { t };
+      SmallHeader { t, caption = "                Syntax:" };
+      MBar { t };
+      CodeBlock { t, minSize = { 900, 550 }, caption =
+         "class MyClass : MyBaseClass\n"
+         "{\n"
+         "   SomeType member1;\n"
+         "\n"
+         "   member1 = someDefaultValue;\n"
+         "\n"
+         "   void someMethod()\n"
+         "   {\n"
+         "   }\n"
+         "}";
+      };
+}
diff --git a/autoLayout/slides/formSlide.ec b/autoLayout/slides/formSlide.ec
new file mode 100644 (file)
index 0000000..40f3f87
--- /dev/null
@@ -0,0 +1,32 @@
+import "base"
+
+class FormSlide : BaseSlide
+{
+   BMBar { this };
+   Title title { this, caption = "A Form class" };
+
+   MBar { this };
+   Col t { this, maxSize = { 1550, 1250 }, margin = { 60, 15, 60, 15 } };
+      //Header { t, caption = "Features" };
+      MBar { t };
+      Bullet { t, caption = "The Ecere library provides a cross-platform GUI toolkit." };
+      Bullet { t, caption = "All controls and form derive from the Window class." };
+      Bullet { t, caption = "Let's define a Form class inheriting from Window:" };
+      BMBar { t };
+      SmallHeader { t, caption = "                Syntax:" };
+      MBar { t };
+      CodeBlock { t, minSize = { 900, 600 }, caption =
+         "import \"ecere\"\n"
+         "class Form : Window\n"
+         "{\n"
+         "   caption = $\"My Form\";\n"
+         "   background = formColor;\n"
+         "   borderStyle = sizable;\n"
+         "   hasMaximize = true;\n"
+         "   hasMinimize = true;\n"
+         "   hasClose = true;\n"
+         "   clientSize = { 640, 480 };\n"
+         "}\n"
+         "Form form { };\n"
+      };
+}
diff --git a/autoLayout/slides/gnosisSlide.ec b/autoLayout/slides/gnosisSlide.ec
new file mode 100644 (file)
index 0000000..6d49e2f
--- /dev/null
@@ -0,0 +1,43 @@
+import "base"
+
+class GNOSISSlide : BaseSlide
+{
+   BMBar { this };
+   Title title { this, caption = "GNOSIS Sample" };
+
+   MBar { this };
+   Col t { this, maxSize = { 1550, 1250 }, margin = { 60, 15, 60, 15 } };
+      //Header { t, caption = "Features" };
+      MBar { t };
+      Bullet { t, caption = "The GNOSIS SDK presents a simple object-oriented API" };
+      Bullet { t, caption = "Key Classes: View3D, MapSource, MapLayer" };
+      MBar { t };
+      CodeBlock { t, minSize = { 1550, 930 }, font = { "Consolas", 20 }, caption =
+         "import \"ecere\"\n"
+         "import \"gnosis2\"\n"
+         "import \"cameraController\"\n"
+         "class MainWindow : Window\n"
+         "{\n"
+         "   displayDriver = \"OpenGL\";\n"
+         "   caption = \"GNOSIS SDK Sample App\";\n"
+         "   background = black;\n"
+         "   borderStyle = sizable;\n"
+         "   hasMaximize = true, hasMinimize = true, hasClose = true;\n"
+         "   clientSize = { 640, 480 };\n"
+         "\n"
+         "   MapSource bmNGSrc;\n"
+         "   bmNGSrc  = \"maps/BlueMarbleNextGen - August 2004\";\n"
+         "\n"
+         "   View3D view { void update() { window.Update(null); } };\n"
+         "   MapLayer bmNG { view, source = bmNGSrc };\n"
+         "\n"
+         "   controller = CameraController\n"
+         "   {\n"
+         "      controlled = view,\n"
+         "      position = { 0, 0, Kilometers { 20000 } };\n"
+         "      orientation = { yaw = 0, pitch = 90, roll = 0 };\n"
+         "   };\n"
+         "}\n"
+         "MainWindow window { };\n"
+      };
+}
diff --git a/autoLayout/slides/instancesSlide.ec b/autoLayout/slides/instancesSlide.ec
new file mode 100644 (file)
index 0000000..5c16eb2
--- /dev/null
@@ -0,0 +1,26 @@
+import "base"
+
+class InstancesSlide : BaseSlide
+{
+   BMBar { this };
+   Title title { this, caption = "Instances" };
+
+   MBar { this };
+   //Col t { this, maxSize = { 1450, 1150 }, margin = { 115, 15, 115, 15 } };
+   Col t { this, maxSize = { 1550, 1250 }, margin = { 60, 15, 60, 15 } };
+      //Header { t, caption = "Features" };
+      BMBar { t };
+      Bullet { t, caption = "Multiple objects can be instantiated from same class" };
+      Bullet { t, caption = "Value of members can be modified from defaults" };
+      Bullet { t, caption = "Virtual methods can be overridden" };
+      BMBar { t };
+      SmallHeader { t, caption = "                Syntax:" };
+      MBar { t };
+      CodeBlock { t, minSize = { 920, 320 }, caption =
+         "// Named Instance (declaration)\n"
+         "MyClass object { member = value };\n"
+         "\n"
+         "// Anonymous Instance (expression)\n"
+         "MyClass { member = value };"
+      };
+}
diff --git a/autoLayout/slides/languageSlide.ec b/autoLayout/slides/languageSlide.ec
new file mode 100644 (file)
index 0000000..beeafe7
--- /dev/null
@@ -0,0 +1,21 @@
+import "base"
+
+class LanguageSlide : BaseSlide
+{
+   BMBar { this };
+   Title title { this, caption = "The eC Language" };
+
+   MBar { this };
+   //Col t { this, maxSize = { 1450, 1150 }, margin = { 115, 15, 115, 15 } };
+   Col t { this, maxSize = { 1550, 1250 }, margin = { 60, 15, 60, 15 } };
+      //Header { t, caption = "Features" };
+      BMBar { t };
+      Bullet { t, caption = "C Superset" };
+      Bullet { t, caption = "C Compatible" };
+      Bullet { t, caption = "Native" };
+      Bullet { t, caption = "Object-Oriented" };
+      Bullet { t, caption = "Properties" };
+      Bullet { t, caption = "Modules" };
+      Bullet { t, caption = "Reflection" };
+      Bullet { t, caption = "Dynamic Imports" };
+}
diff --git a/autoLayout/slides/slide1.ec b/autoLayout/slides/slide1.ec
new file mode 100644 (file)
index 0000000..3247c40
--- /dev/null
@@ -0,0 +1,28 @@
+import "base"
+
+class Slide1 : BaseSlide
+{
+   BMBar { this };
+   Element title { this, maxSize = { 1430, 0 }, font = { "Verdana", 50, bold = true }, caption = "eC - A crash course", fgColor = textColor };
+
+   MBar { this };
+   Col t { this, maxSize = { 1400, 1100 }, margin = { 15, 15, 15, 15 } };
+      Header { t, caption = "eC Language" };
+      Bullet { t, caption = "Classes" };
+      Bullet { t, caption = "Instances" };
+      Bullet { t, caption = "Methods" };
+      Bullet { t, caption = "Flow Control" };
+      Bullet { t, caption = "Variables" };
+      Bullet { t, caption = "Style Guidelines" };
+      Bullet { t, caption = "Member Instance" };
+      Bullet { t, caption = "Instance method" };
+      Bullet { t, caption = "Setting properties" };
+      Bullet { t, caption = "Default properties" };
+      MBar { t };
+      Header { t, caption = "Ecere GUI" };
+      Bullet { t, caption = "Creating a Form" };
+      Bullet { t, caption = "Positions and Anchors" };
+      Bullet { t, caption = "Button: Responding to a click" };
+      Bullet { t, caption = "MessageBox and Modal Loops" };
+      Bullet { t, caption = "This is very long text that would ideally have automatic line-wrapping onto the next line. Now we have to implement this into autoLayout.ec." };
+}
diff --git a/autoLayout/slides/titleSlide.ec b/autoLayout/slides/titleSlide.ec
new file mode 100644 (file)
index 0000000..818feab
--- /dev/null
@@ -0,0 +1,9 @@
+import "base"
+
+class TitleSlide : BaseSlide
+{
+   vAlignment = center;
+   Element title { this, font = { "Verdana", 100, bold = true }, caption = "eC", fgColor = textColor };
+   Element subTitle { this, font = { "Verdana", 80, italic = true }, caption = "An introduction", fgColor = textColor };
+   MBar { this, maxSize.h = 600 };
+}