Kaynağa Gözat

SimpleGui: Redraw mechanism; moved to Game subdirectory

Arthur Yefimov 1 yıl önce
ebeveyn
işleme
ca6ba381dc

BIN
Programs/Examples/Game/Data/Graph/tiles.png


+ 80 - 0
Programs/Examples/Game/GameEngine.Mod

@@ -0,0 +1,80 @@
+MODULE GameEngine;
+IMPORT G := Graph, Random, Out, Strings;
+
+CONST
+  maxMapW* = 128;
+  maxMapH* = maxMapW;
+
+  cellW* = 16;
+  cellH* = cellW;
+
+  tilesInRow* = 8;
+
+TYPE
+  Cell* = RECORD (** A single cell of the map *)
+    kind*: INTEGER
+  END;
+
+  Map* = RECORD
+    w*, h*: INTEGER;
+    cells*: ARRAY maxMapH, maxMapW OF Cell
+  END;
+
+  Game* = RECORD
+    map*: Map
+  END;
+
+VAR
+  tiles*: G.Bitmap;
+
+PROCEDURE MakeRandomMap*(VAR map: Map; w, h: INTEGER);
+VAR x, y: INTEGER;
+BEGIN
+  map.w := w; map.h := h;
+  FOR y := 0 TO h - 1 DO
+    FOR x := 0 TO w - 1 DO
+      map.cells[y, x].kind := Random.Int(4)
+    END
+  END
+END MakeRandomMap;
+
+PROCEDURE LoadMap*(VAR map: Map; fname: ARRAY OF CHAR): BOOLEAN;
+BEGIN
+
+RETURN TRUE END LoadMap;
+
+PROCEDURE SaveMap*(VAR map: Map; fname: ARRAY OF CHAR);
+BEGIN
+
+END SaveMap;
+
+(** Returns a bitmap with the given name (appends strings to make a file name).
+    On error sets ok to FALSE and ouputs an error message.
+    Never sets ok to TRUE. *)
+PROCEDURE LoadBitmap(name: ARRAY OF CHAR; VAR ok: BOOLEAN): G.Bitmap;
+VAR bmp: G.Bitmap;
+  s: ARRAY 256 OF CHAR;
+BEGIN
+  s := 'Data/Graph/';
+  Strings.Append(name, s);
+  Strings.Append('.png', s);
+  bmp := G.LoadBitmap(s);
+  IF bmp = NIL THEN
+    ok := FALSE;
+    Out.String('Error: Could not load bitmap "');
+    Out.String(s); Out.String('".'); Out.Ln
+  END
+RETURN bmp END LoadBitmap;
+
+PROCEDURE InitGame*(VAR game: Game);
+BEGIN
+  MakeRandomMap(game.map, 96, 96)
+END InitGame;
+
+PROCEDURE Init*(): BOOLEAN;
+VAR ok: BOOLEAN;
+BEGIN ok := TRUE;
+  tiles := LoadBitmap('tiles', ok)
+RETURN ok END Init;
+
+END GameEngine.

+ 40 - 14
Programs/Examples/MapEditor.Mod → Programs/Examples/Game/MapEditor.Mod

@@ -1,6 +1,5 @@
 MODULE MapEditor;
-IMPORT G := Graph, S := SimpleGui, Out, Int, Strings;
-
+IMPORT G := Graph, S := SimpleGui, Out, Int, Strings, E := GameEngine;
 CONST window = FALSE;
 VAR
   frmMain: S.Form;
@@ -15,22 +14,39 @@ VAR
   pnlSide: S.Panel;
 
   sbxMap: S.ScrollBox;
-  map: S.Widget;
+  wgtMap: S.Widget;
+  cellX, cellY: INTEGER;
+
+  game: E.Game;
 
 PROCEDURE BtnExitOnClick(c: S.Widget);
 BEGIN
   S.Quit
 END BtnExitOnClick;
 
-PROCEDURE MapOnPaint(c: S.Widget; x, y, w, h: INTEGER);
-VAR color: G.Color;
+PROCEDURE DrawCell(cell: E.Cell; x, y, toX, toY: INTEGER);
+VAR kx, ky: INTEGER;
+BEGIN
+  kx := cell.kind MOD E.tilesInRow * E.cellW;
+  ky := cell.kind DIV E.tilesInRow * E.cellH;
+  G.DrawPart(E.tiles, kx, ky, E.cellW, E.cellH, toX, toY);
+END DrawCell;
+
+PROCEDURE WgtMapOnPaint(c: S.Widget; x, y, w, h: INTEGER);
+VAR X, Y, i, j: INTEGER;
 BEGIN
-  G.MakeCol(color, 255, 255, 0);
-  G.Line(x, y, x + w - 1, y + h - 1, color);
-  G.Line(x + w - 1, y, x, y + h - 1, color);
-END MapOnPaint;
+  Y := y;
+  FOR i := 0 TO game.map.h - 1 DO
+    X := x;
+    FOR j := 0 TO game.map.w - 1 DO
+      DrawCell(game.map.cells[i, j], i, j, X, Y);
+      INC(X, E.cellW)
+    END;
+    INC(Y, E.cellH)
+  END
+END WgtMapOnPaint;
 
-PROCEDURE SbxMapOnMouseMove(c: S.Widget; x, y: INTEGER; btns: SET);
+PROCEDURE WgtMapOnMouseMove(c: S.Widget; x, y: INTEGER; btns: SET);
 VAR color: G.Color;
   draw: BOOLEAN;
 BEGIN
@@ -46,7 +62,13 @@ BEGIN
     G.Target(c(S.Canvas).bmp);
     G.PutPixel(x, y, color)
   END
-END SbxMapOnMouseMove;
+END WgtMapOnMouseMove;
+
+PROCEDURE InitMapWidgetVars;
+BEGIN
+  cellX := -1;
+  cellY := 0
+END InitMapWidgetVars;
 
 PROCEDURE InitInterface(): BOOLEAN;
 VAR W, H: INTEGER;
@@ -80,10 +102,12 @@ BEGIN
       W - pnlSide.w, H - pnlTop.h);
   S.ScrollBoxSetInnerSize(sbxMap, 1024, 1024);
   G.MakeCol(color, 0, 0, 0);
-  S.SetBgColor(sbxMap, color);
-  (*S.ScrollBoxSetNoBg(sbxMap, TRUE);*)
-  (*S.SetOnMouseMove(sbxMap, SbxMapOnMouseMove);*)
+  S.ScrollBoxSetNoBg(sbxMap, TRUE);
 
+  wgtMap := S.NewWidget(sbxMap, 0, 0, 1024, 1024);
+  S.SetOnPaint(wgtMap, WgtMapOnPaint);
+  (*S.SetOnMouseMove(wgtMap, WgtMapOnMouseMove);*)
+  InitMapWidgetVars
 RETURN TRUE END InitInterface;
 
 PROCEDURE Init(): BOOLEAN;
@@ -96,6 +120,8 @@ BEGIN ok := TRUE;
     S.Init;
     IF ~S.Done THEN ok := FALSE END
   END;
+  IF ok & ~E.Init() THEN ok := FALSE END;
+  IF ok THEN E.InitGame(game) END;
   IF ok & ~InitInterface() THEN ok := FALSE END
 RETURN ok END Init;
 

+ 140 - 85
Programs/Examples/SimpleGui.Mod → Programs/Examples/Game/SimpleGui.Mod

@@ -35,6 +35,8 @@ TYPE
   WidgetDesc* = RECORD
     x*, y*, w*, h*: INTEGER;
     bgColor*, fgColor*: G.Color;
+    redraw*: BOOLEAN; (** TRUE if widget or it's insides need to be redrawn *)
+    redrawSelf*: BOOLEAN; (** TRUE if widget itself needs to be redrawn *)
     focusable*: BOOLEAN; (** TRUE if widget can get focus *)
     focused*: BOOLEAN; (** TRUE if this widget is globally in focus *)
     hovered*: BOOLEAN; (** TRUE if mouse pointer is over the widget *)
@@ -127,6 +129,18 @@ VAR
 
 (** Widget **)
 
+PROCEDURE Redraw*(c: Widget);
+VAR p: Widget;
+BEGIN
+  c.redraw := TRUE; c.redrawSelf := TRUE;
+  p := c.parent;
+  WHILE p # NIL DO p.redraw := TRUE; p := p.parent END
+END Redraw;
+
+PROCEDURE Drawn*(c: Widget);
+BEGIN c.redraw := FALSE; c.redrawSelf := FALSE
+END Drawn;
+
 PROCEDURE FindHoveredInRing(list: Widget; x, y: INTEGER;
     forMouseDown: BOOLEAN): Widget;
 VAR c: Widget;
@@ -207,14 +221,16 @@ PROCEDURE Focus*(c: Widget);
 VAR get: GetFocusMsg;
   lost: LostFocusMsg;
 BEGIN
-  IF c.focusable THEN
+  IF ((c = NIL) OR c.focusable) & (focusedWidget # c) THEN
     IF focusedWidget # NIL THEN
       focusedWidget.focused := FALSE;
       focusedWidget.handle(focusedWidget, lost)
     END;
-    c.focused := TRUE;
-    focusedWidget := c;
-    focusedWidget.handle(focusedWidget, get)
+    IF c # NIL THEN
+      c.focused := TRUE;
+      focusedWidget := c;
+      focusedWidget.handle(focusedWidget, get)
+    END
   END
 END Focus;
 
@@ -312,19 +328,26 @@ PROCEDURE WidgetHandler*(c: Widget; VAR msg: Message);
 VAR x, y: INTEGER;
 BEGIN
   IF msg IS DrawMsg THEN
-    x := msg(DrawMsg).x; y := msg(DrawMsg).y;
-    IF c.onPaint # NIL THEN
-      c.onPaint(c, x, y, msg(DrawMsg).w, msg(DrawMsg).h)
-    ELSE
-      G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
-      G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
-      G.Rect(x + 2, y + 2, x + c.w - 3, y + c.h - 3, c.fgColor)
+    IF c.redraw THEN
+      IF c.redrawSelf THEN
+        x := msg(DrawMsg).x; y := msg(DrawMsg).y;
+        IF c.onPaint # NIL THEN
+          c.onPaint(c, x, y, msg(DrawMsg).w, msg(DrawMsg).h)
+        ELSE
+          G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
+          G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
+          G.Rect(x + 2, y + 2, x + c.w - 3, y + c.h - 3, c.fgColor)
+        END;
+        Drawn(c)
+      END
     END
   ELSIF msg IS MouseDownMsg THEN
     IF msg(MouseDownMsg).btn = 1 THEN c.pressed := TRUE END
   ELSIF msg IS MouseUpMsg THEN c.pressed := FALSE
   ELSIF msg IS PutMsg THEN
     DirectPut(msg(PutMsg).what, c, msg(PutMsg).x, msg(PutMsg).y)
+  ELSIF msg IS GetFocusMsg THEN Redraw(c)
+  ELSIF msg IS LostFocusMsg THEN Redraw(c)
   END
 END WidgetHandler;
 
@@ -349,17 +372,19 @@ BEGIN
     IF CX < x THEN DEC(CW, x - CX); CX := x END;
     IF CY < y THEN DEC(CH, y - CY); CY := y END;
     REPEAT
-      x2 := x + p.x; y2 := y + p.y;
-      w2 := w - p.x; h2 := h - p.y;
-
-      cx := x2; cy := y2; cw := p.w; ch := p.h;
-      IF cx + cw > CX + CW THEN cw := CX + CW - cx END;
-      IF cy + ch > CY + CH THEN ch := CY + CH - cy END;
-      IF cx < CX THEN DEC(cw, CX - cx); cx := CX END;
-      IF cy < CY THEN DEC(ch, CY - cy); cy := CY END;
-      G.SetClip(cx, cy, cw, ch);
-
-      DrawWidget(p, x2, y2, p.w, p.h);
+      IF p.redraw THEN
+        x2 := x + p.x; y2 := y + p.y;
+        w2 := w - p.x; h2 := h - p.y;
+
+        cx := x2; cy := y2; cw := p.w; ch := p.h;
+        IF cx + cw > CX + CW THEN cw := CX + CW - cx END;
+        IF cy + ch > CY + CH THEN ch := CY + CH - cy END;
+        IF cx < CX THEN DEC(cw, CX - cx); cx := CX END;
+        IF cy < CY THEN DEC(ch, CY - cy); cy := CY END;
+        G.SetClip(cx, cy, cw, ch);
+
+        DrawWidget(p, x2, y2, p.w, p.h)
+      END;
       p := p.next
     UNTIL p = c.body;
     G.UnsetClip
@@ -396,6 +421,7 @@ END SetOnClick;
 
 PROCEDURE InitWidget*(c: Widget; w, h: INTEGER);
 BEGIN c.x := 0; c.y := 0; c.w := w; c.h := h;
+  c.redraw := TRUE; c.redrawSelf := TRUE;
   c.focusable := FALSE; c.focused := FALSE;
   c.hovered := FALSE; c.pressed := FALSE;
   G.MakeCol(c.bgColor, 180, 180, 180);
@@ -413,18 +439,21 @@ RETURN c END NewWidget;
 (** Panel **)
 
 PROCEDURE PanelSetNoBg*(c: Panel; noBg: BOOLEAN);
-BEGIN c.noBg := noBg
+BEGIN c.noBg := noBg; Redraw(c)
 END PanelSetNoBg;
 
 PROCEDURE PanelHandler*(c: Widget; VAR msg: Message);
 VAR x, y: INTEGER;
 BEGIN
   IF msg IS DrawMsg THEN
-    x := msg(DrawMsg).x; y := msg(DrawMsg).y;
-    IF ~c(Panel).noBg THEN
-      G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor)
-    END;
-    DrawBody(c, x, y, c.w, c.h)
+    IF c.redraw THEN
+      x := msg(DrawMsg).x; y := msg(DrawMsg).y;
+      IF c.redrawSelf & ~c(Panel).noBg THEN
+        G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor)
+      END;
+      DrawBody(c, x, y, c.w, c.h);
+      Drawn(c)
+    END
   ELSE WidgetHandler(c, msg)
   END
 END PanelHandler;
@@ -459,8 +488,13 @@ RETURN c END NewApp;
 
 PROCEDURE DrawForm*(c: Form);
 BEGIN
-  G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor);
-  DrawBody(c, c.x, c.y, c.w, c.h)
+  IF c.redraw THEN
+    IF c.redrawSelf THEN
+      G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor)
+    END;
+    DrawBody(c, c.x, c.y, c.w, c.h);
+    Drawn(c)
+  END
 END DrawForm;
 
 PROCEDURE FormHandler*(c: Widget; VAR msg: Message);
@@ -546,14 +580,17 @@ END DrawButtonBox;
 PROCEDURE DrawButton*(c: Button; x, y, w, h: INTEGER);
 VAR fw, fh, tw, tx, ty: INTEGER;
 BEGIN
-  DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor,
-      c.pressed & c.hovered, TRUE);
-  G.GetMonoFontSize(font, fw, fh);
-  tw := Strings.Length(c.caption) * fw;
-  tx := x + (c.w - tw) DIV 2;
-  ty := y + (c.h - fh) DIV 2;
-  IF c.pressed & c.hovered THEN INC(tx); INC(ty) END;
-  G.DrawString(c.caption, tx, ty, font, c.fgColor)
+  IF c.redraw THEN
+    DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor,
+        c.pressed & c.hovered, TRUE);
+    G.GetMonoFontSize(font, fw, fh);
+    tw := Strings.Length(c.caption) * fw;
+    tx := x + (c.w - tw) DIV 2;
+    ty := y + (c.h - fh) DIV 2;
+    IF c.pressed & c.hovered THEN INC(tx); INC(ty) END;
+    G.DrawString(c.caption, tx, ty, font, c.fgColor);
+    Drawn(c)
+  END
 END DrawButton;
 
 PROCEDURE ButtonHandler*(c: Widget; VAR msg: Message);
@@ -561,7 +598,7 @@ VAR b: Button;
 BEGIN b := c(Button);
   IF msg IS DrawMsg THEN
     DrawButton(b, msg(DrawMsg).x, msg(DrawMsg).y,
-        msg(DrawMsg).w, msg(DrawMsg).h)
+        msg(DrawMsg).w, msg(DrawMsg).h);
   ELSE WidgetHandler(c, msg)
   END
 END ButtonHandler;
@@ -585,18 +622,21 @@ RETURN c END NewButton;
 PROCEDURE DrawLabel*(c: Label; x, y, w, h: INTEGER);
 VAR fw, fh, tw, tx, ty: INTEGER;
 BEGIN
-  G.GetMonoFontSize(font, fw, fh);
-  tx := 0;
-  IF c.align # alLeft THEN
-    tw := Strings.Length(c.caption) * fw;
-    IF c.align = alCenter THEN
-      tx := (c.w - tw) DIV 2
-    ELSIF c.align = alRight THEN
-      tx := c.w - tw
-    END
-  END;
-  ty := y + (c.h - fh) DIV 2;
-  G.DrawString(c.caption, tx, ty, font, c.fgColor)
+  IF c.redraw THEN
+    G.GetMonoFontSize(font, fw, fh);
+    tx := 0;
+    IF c.align # alLeft THEN
+      tw := Strings.Length(c.caption) * fw;
+      IF c.align = alCenter THEN
+        tx := (c.w - tw) DIV 2
+      ELSIF c.align = alRight THEN
+        tx := c.w - tw
+      END
+    END;
+    ty := y + (c.h - fh) DIV 2;
+    G.DrawString(c.caption, tx, ty, font, c.fgColor);
+    Drawn(c)
+  END
 END DrawLabel;
 
 PROCEDURE LabelHandler*(c: Widget; VAR msg: Message);
@@ -625,11 +665,11 @@ BEGIN NEW(c); InitLabel(c, where, x, y, w, h, caption)
 RETURN c END NewLabel;
 
 PROCEDURE LabelSetCaption*(c: Label; caption: ARRAY OF CHAR);
-BEGIN Strings.Copy(caption, c.caption)
+BEGIN Strings.Copy(caption, c.caption); Redraw(c)
 END LabelSetCaption;
 
 PROCEDURE LabelSetAlign*(c: Label; align: INTEGER);
-BEGIN c.align := align
+BEGIN c.align := align; Redraw(c)
 END LabelSetAlign;
 
 (** Edit **)
@@ -638,24 +678,27 @@ PROCEDURE DrawEdit*(c: Edit; x, y, w, h: INTEGER);
 VAR fw, fh, tw, tx, ty: INTEGER;
   or, yw: G.Color;
 BEGIN
-  MakeOrAndYw(c.parent.bgColor, or, yw);
+  IF c.redraw THEN
+    MakeOrAndYw(c.parent.bgColor, or, yw);
 
-  G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
-  G.GetMonoFontSize(font, fw, fh);
-  tw := Strings.Length(c.text) * fw;
-  tx := x + 2 - c.off;
-  ty := y + (c.h - fh) DIV 2;
-  G.DrawString(c.text, tx, ty, font, c.fgColor);
-  IF c.focused THEN
-    INC(tx, fw * c.pos - 1);
-    G.VLine(tx, ty, ty + fh - 1, or);
-    G.HLine(tx - 1, ty, tx + 1, or);
-    G.HLine(tx - 1, ty + fh - 1, tx + 1, or)
-  END;
-  G.HLine(x, y, x + c.w - 2, c.fgColor);
-  G.VLine(x, y, y + c.h - 1, c.fgColor);
-  G.HLine(x + 1, y + c.h - 1, x + c.w - 1, or);
-  G.VLine(x + c.w - 1, y, y + c.h - 1, or)
+    G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
+    G.GetMonoFontSize(font, fw, fh);
+    tw := Strings.Length(c.text) * fw;
+    tx := x + 2 - c.off;
+    ty := y + (c.h - fh) DIV 2;
+    G.DrawString(c.text, tx, ty, font, c.fgColor);
+    IF c.focused THEN
+      INC(tx, fw * c.pos - 1);
+      G.VLine(tx, ty, ty + fh - 1, or);
+      G.HLine(tx - 1, ty, tx + 1, or);
+      G.HLine(tx - 1, ty + fh - 1, tx + 1, or)
+    END;
+    G.HLine(x, y, x + c.w - 2, c.fgColor);
+    G.VLine(x, y, y + c.h - 1, c.fgColor);
+    G.HLine(x + 1, y + c.h - 1, x + c.w - 1, or);
+    G.VLine(x + c.w - 1, y, y + c.h - 1, or);
+    Drawn(c)
+  END
 END DrawEdit;
 
 PROCEDURE EditOnMouseDown*(c: Edit; VAR msg: MouseDownMsg);
@@ -667,7 +710,10 @@ BEGIN
     G.GetMonoFontSize(font, fw, fh);
     n := (msg.x - 2 + fw DIV 2) DIV fw;
     IF n < 0 THEN n := 0 ELSIF n > c.len THEN n := c.len END;
-    c.pos := n
+    IF c.pos # n THEN
+      c.pos := n;
+      Redraw(c)
+    END
   END
 END EditOnMouseDown;
 
@@ -713,7 +759,8 @@ BEGIN
     c.text[c.pos] := msg.ch;
     INC(c.len); INC(c.pos)
   END;
-  EditCheckOffset(c)
+  EditCheckOffset(c);
+  Redraw(c)
 END EditOnChar;
 
 PROCEDURE EditHandler*(c: Widget; VAR msg: Message);
@@ -747,7 +794,8 @@ BEGIN
   Strings.Copy(text, c.text);
   c.len := Strings.Length(text);
   c.pos := 0;
-  c.off := 0
+  c.off := 0;
+  Redraw(c)
 END EditSetText;
 
 (** ScrollBar **)
@@ -843,11 +891,12 @@ BEGIN
     DrawVertScrollBar(c, x, y, w, h)
   ELSE
     DrawHorizScrollBar(c, x, y, w, h)
-  END
+  END;
+  Drawn(c)
 END DrawScrollBar;
 
 PROCEDURE ScrollBarSetVertical*(c: ScrollBar; vertical: BOOLEAN);
-BEGIN c.vertical := vertical
+BEGIN c.vertical := vertical; Redraw(c)
 END ScrollBarSetVertical;
 
 PROCEDURE ScrollBarSetValue*(c: ScrollBar; value: INTEGER);
@@ -857,7 +906,8 @@ BEGIN
   END;
   IF c.value # value THEN
     c.value := value;
-    IF c.onScroll # NIL THEN c.onScroll(c, value) END
+    IF c.onScroll # NIL THEN c.onScroll(c, value) END;
+    Redraw(c)
   END
 END ScrollBarSetValue;
 
@@ -944,11 +994,9 @@ BEGIN PanelSetNoBg(c.inner, noBg)
 END ScrollBoxSetNoBg;
 
 PROCEDURE ScrollBoxHandler*(c: Widget; VAR msg: Message);
-VAR x, y: INTEGER;
 BEGIN
   IF msg IS DrawMsg THEN
-    x := msg(DrawMsg).x; y := msg(DrawMsg).y;
-    DrawBody(c, x, y, c.w, c.h)
+    DrawBody(c, msg(DrawMsg).x, msg(DrawMsg).y, c.w, c.h)
   ELSIF msg IS PutMsg THEN
     DirectPut(msg(PutMsg).what, c(ScrollBox).inner,
         msg(PutMsg).x, msg(PutMsg).y)
@@ -960,21 +1008,24 @@ PROCEDURE ScrollBoxSetInnerSize*(c: ScrollBox; w, h: INTEGER);
 BEGIN
   Resize(c.inner, w, h);
   c.scbHoriz.max := w - c.outer.w;
-  c.scbVert.max := h - c.outer.h
+  c.scbVert.max := h - c.outer.h;
+  Redraw(c)
 END ScrollBoxSetInnerSize;
 
 PROCEDURE ScrollBoxOnHorizScroll*(c: ScrollBar; value: INTEGER);
 VAR sbx: ScrollBox;
 BEGIN
   sbx := c.parent(ScrollBox);
-  sbx.inner.x := -value
+  sbx.inner.x := -value;
+  Redraw(c)
 END ScrollBoxOnHorizScroll;
 
 PROCEDURE ScrollBoxOnVertScroll*(c: ScrollBar; value: INTEGER);
 VAR sbx: ScrollBox;
 BEGIN
   sbx := c.parent(ScrollBox);
-  sbx.inner.y := -value
+  sbx.inner.y := -value;
+  Redraw(c)
 END ScrollBoxOnVertScroll;
 
 PROCEDURE InitScrollBox*(c: ScrollBox; where: Widget; x, y, w, h: INTEGER);
@@ -1011,9 +1062,12 @@ PROCEDURE CanvasHandler*(c: Widget; VAR msg: Message);
 VAR x, y: INTEGER;
 BEGIN
   IF msg IS DrawMsg THEN
-    x := msg(DrawMsg).x; y := msg(DrawMsg).y;
-    G.Draw(c(Canvas).bmp, x, y);
-    DrawBody(c, x, y, c.w, c.h)
+    IF c.redraw THEN
+      x := msg(DrawMsg).x; y := msg(DrawMsg).y;
+      IF c.redrawSelf THEN G.Draw(c(Canvas).bmp, x, y) END;
+      DrawBody(c, x, y, c.w, c.h);
+      Drawn(c)
+    END
   ELSE WidgetHandler(c, msg)
   END
 END CanvasHandler;
@@ -1185,6 +1239,7 @@ END InitCursor;
 PROCEDURE Init*;
 BEGIN
   font := G.LoadFont('Data/Fonts/Main');
+  IF font = NIL THEN font := G.LoadFont('../Data/Fonts/Main') END;
   IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
   InitCursor;
   Done := font # NIL;