WMScrollableComponents.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. MODULE WMScrollableComponents; (** AUTHOR "Ingmar Nebel"; PURPOSE "Scrollable Container"; *)
  2. IMPORT
  3. Strings, XML, WMGraphics, WMRectangles, WMMessages, WMProperties, WMComponents, WMStandardComponents;
  4. TYPE
  5. (* Local type-alias for convenience *)
  6. String = Strings.String;
  7. Panel = WMStandardComponents.Panel;
  8. (* special component to adapt to scroll target, in total conrol by ScrollablePanel, never use otherwise *)
  9. ScrollPanel*= OBJECT(WMComponents.VisualComponent)
  10. VAR
  11. left, top, dx, dy: LONGINT;
  12. CheckScrollbars: WMMessages.CompCommand;
  13. resizing: BOOLEAN; (* distinguish whether AlignSubComponents is called from children or from Resized *)
  14. PROCEDURE &New*(CheckScrollbars: WMMessages.CompCommand);
  15. BEGIN
  16. Init;
  17. SELF.CheckScrollbars := CheckScrollbars;
  18. left := 0; top := 0;
  19. SetNameAsString(StrScrollPanel);
  20. END New;
  21. (* store total width and height of subcomponents, check it *)
  22. PROCEDURE AlignSubComponents*;
  23. VAR c: XML.Content; vc : WMComponents.VisualComponent;
  24. r, rCopy, rEnclosing, vcBounds, b : WMRectangles.Rectangle;
  25. BEGIN
  26. Acquire;
  27. IF aligning THEN Release; RETURN END;
  28. aligning := TRUE;
  29. r := GetClientRect(); rCopy := r; rEnclosing := r;
  30. c := GetFirst();
  31. WHILE (c # NIL) DO
  32. IF c IS WMComponents.VisualComponent THEN
  33. vc := c(WMComponents.VisualComponent);
  34. IF vc.visible.Get() THEN
  35. b := vc.bearing.Get();
  36. CASE vc.alignment.Get() OF
  37. | WMComponents.AlignTop : vc.bounds.Set(WMRectangles.MakeRect(r.l + b.l , r.t + b.t, r.r - b.r, r.t + b.t + vc.bounds.GetHeight())); INC(r.t, vc.bounds.GetHeight() + b.t + b.b);
  38. | WMComponents.AlignLeft : vc.bounds.Set(WMRectangles.MakeRect(r.l + b.l, r.t + b.t, r.l + b.l + vc.bounds.GetWidth(), r.b - b.b)); INC(r.l, vc.bounds.GetWidth() + b.l + b.r)
  39. | WMComponents.AlignBottom : vc.bounds.Set(WMRectangles.MakeRect(r.l + b.l, r.b - vc.bounds.GetHeight() - b.b, r.r - b.r, r.b - b.b)); DEC(r.b, vc.bounds.GetHeight() + b.t + b.b)
  40. | WMComponents.AlignRight : vc.bounds.Set(WMRectangles.MakeRect(r.r - vc.bounds.GetWidth() - b.r , r.t + b.t, r.r - b.r, r.b - b.b)); DEC(r.r, vc.bounds.GetWidth() + b.l + b.r);
  41. | WMComponents.AlignClient : IF ~WMRectangles.RectEmpty(r) THEN vc.bounds.Set(WMRectangles.MakeRect(r.l + b.l , r.t + b.t, r.r - b.r, r.b - b.b)) END
  42. ELSE (* nothing *)
  43. END;
  44. vcBounds := vc.bounds.Get();
  45. WMRectangles.ExtendRect(rEnclosing, vcBounds);
  46. END
  47. END;
  48. c := GetNext(c);
  49. END;
  50. dx := MAX(0, (rEnclosing.r-rEnclosing.l)-(rCopy.r-rCopy.l));
  51. dy := MAX(0, (rEnclosing.b-rEnclosing.t)-(rCopy.b-rCopy.t));
  52. CheckLeftTop;
  53. aligning := FALSE;
  54. Release;
  55. IF ~resizing THEN CheckScrollbars(NIL, NIL) END;
  56. END AlignSubComponents;
  57. PROCEDURE CheckLeftTop;
  58. BEGIN
  59. left := MIN(left, dx);
  60. top := MIN(top, dy);
  61. END CheckLeftTop;
  62. PROCEDURE SetLeftTop(dxf, dyf: REAL);
  63. BEGIN
  64. SELF.left := ENTIER(dx * dxf); SELF.top := ENTIER(dy * dyf); CheckLeftTop;
  65. END SetLeftTop;
  66. (** Special methods *)
  67. PROCEDURE Resized*;
  68. BEGIN
  69. IF sequencer # NIL THEN ASSERT(sequencer.lock.HasWriteLock()) END;
  70. resizing := TRUE;
  71. DisableUpdate;
  72. (* don't need to adjust parent, because bounds are always changed by parent, not third party
  73. p := SELF.GetParent();
  74. IF (p # NIL) & (p IS VisualComponent) THEN p(VisualComponent).AlignSubComponents END;
  75. *)
  76. AlignSubComponents;
  77. EnableUpdate;
  78. (*IF (p # NIL) & (p IS VisualComponent) THEN p(VisualComponent).Invalidate
  79. ELSE Invalidate()
  80. END*)
  81. resizing := FALSE;
  82. Invalidate;
  83. END Resized;
  84. (** declare a rectangle area as dirty *)
  85. PROCEDURE InvalidateRect*(r: WMRectangles.Rectangle);
  86. VAR parent : XML.Element;
  87. m : WMMessages.Message; b, cr : WMRectangles.Rectangle;
  88. BEGIN
  89. IF ~initialized THEN RETURN END;
  90. IF ~visible.Get() THEN RETURN END;
  91. IF ~IsCallFromSequencer() THEN
  92. m.msgType := WMMessages.MsgExt;
  93. m.ext := WMComponents.invalidateRectMsg; m.x := r.l; m.y := r.t; m.dx := r.r; m.dy := r.b; m.sender := SELF;
  94. IF ~sequencer.Add(m) THEN END;
  95. ELSE
  96. parent := GetParent();
  97. IF (parent # NIL) & (parent IS WMComponents.VisualComponent) THEN
  98. cr := GetClientRect();
  99. WMRectangles.MoveRel(r, -left, -top);
  100. WMRectangles.ClipRect(r, cr);
  101. IF ~WMRectangles.RectEmpty(r) THEN
  102. b := bounds.Get();
  103. WMRectangles.MoveRel(r, b.l, b.t);
  104. parent(WMComponents.VisualComponent).InvalidateRect(r)
  105. END
  106. END
  107. END
  108. END InvalidateRect;
  109. PROCEDURE InvalidateCommand*(sender, par : ANY);
  110. VAR cr: WMRectangles.Rectangle;
  111. BEGIN
  112. IF ~initialized THEN RETURN END;
  113. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.InvalidateCommand, sender, par)
  114. ELSIF visible.Get() THEN
  115. cr := GetClientRect(); WMRectangles.MoveRel(cr, left, top);
  116. InvalidateRect(cr)
  117. END
  118. END InvalidateCommand;
  119. PROCEDURE HandleInternal*(VAR msg : WMMessages.Message); (** PROTECTED *)
  120. BEGIN
  121. ASSERT(IsCallFromSequencer());
  122. IF (msg.msgType = WMMessages.MsgPointer) OR (msg.msgType = WMMessages.MsgDrag) THEN
  123. msg.x := msg.x + left; msg.y := msg.y + top;
  124. END;
  125. HandleInternal^(msg);
  126. END HandleInternal;
  127. PROCEDURE Draw*(canvas : WMGraphics.Canvas);
  128. VAR canvasState: WMGraphics.CanvasState;
  129. BEGIN
  130. canvas.SaveState(canvasState);
  131. canvas.SetDelta(canvas.dx - left, canvas.dy - top);
  132. DrawSubComponents(canvas);
  133. canvas.RestoreState(canvasState)
  134. END Draw;
  135. END ScrollPanel;
  136. TYPE
  137. (** just shows an image, showing scrollbars if necessairy *)
  138. ScrollableContainer* = OBJECT(Panel)
  139. VAR
  140. vScrollbar, hScrollbar : WMStandardComponents.Scrollbar;
  141. scrollPanel: ScrollPanel;
  142. dx, dy : LONGINT;
  143. minNofLevels*, nofLevelsPerPage* : WMProperties.Int32Property;
  144. wheelScrolling- : WMProperties.BooleanProperty;
  145. PROCEDURE & Init*;
  146. BEGIN
  147. Init^;
  148. SetGenerator("WMScrollableComponents.GenScrollableContainer");
  149. (* scrollbars *)
  150. NEW(vScrollbar); vScrollbar.alignment.Set(WMComponents.AlignRight); AddInternalComponent^(vScrollbar);
  151. vScrollbar.onPositionChanged.Add(ScrollbarsChanged); vScrollbar.visible.Set(FALSE);
  152. NEW(hScrollbar); hScrollbar.alignment.Set(WMComponents.AlignBottom); AddInternalComponent^(hScrollbar);
  153. hScrollbar.vertical.Set(FALSE); hScrollbar.onPositionChanged.Add(ScrollbarsChanged);
  154. hScrollbar.visible.Set(FALSE);
  155. NEW(scrollPanel, FitScrollTarget); scrollPanel.alignment.Set(WMComponents.AlignClient); AddInternalComponent^(scrollPanel);
  156. SetNameAsString(StrScrollableContainer);
  157. dx := 0; dy := 0 ;
  158. NEW(minNofLevels, PrototypeSCMinNofLevels, NIL, NIL); properties.Add(minNofLevels);
  159. NEW(nofLevelsPerPage, PrototypeSCNofLevelsPerPage, NIL, NIL); properties.Add(nofLevelsPerPage);
  160. NEW(wheelScrolling, PrototypeSCWheelScrolling, NIL, NIL); properties.Add(wheelScrolling);
  161. END Init;
  162. PROCEDURE AlignSubComponents*;
  163. BEGIN
  164. (* align scrollbars and scrollPanel first *)
  165. Acquire;
  166. IF aligning THEN Release; RETURN END;
  167. AlignSubComponents^;
  168. (* the own bounds or client bounds may have changed *)
  169. aligning := TRUE;
  170. FitScrollTarget(NIL, NIL);
  171. aligning := FALSE;
  172. Release;
  173. END AlignSubComponents;
  174. PROCEDURE HandleInternal*(VAR msg : WMMessages.Message);
  175. BEGIN
  176. IF wheelScrolling.Get() & (msg.msgType = WMMessages.MsgPointer) & (msg.msgSubType = WMMessages.MsgSubPointerMove) & (msg.dz # 0) THEN
  177. WheelMove(msg.dz);
  178. msg.dz := 0;
  179. END;
  180. HandleInternal^(msg);
  181. END HandleInternal;
  182. PROCEDURE FitScrollTarget(sender, par: ANY);
  183. VAR spw, sph, tw, th, sw, sh, w, h, rw, rh, nofLevels: LONGINT;
  184. BEGIN
  185. IF (sequencer # NIL) & ~sequencer.IsCallFromSequencer() THEN
  186. sequencer.ScheduleEvent(FitScrollTarget, NIL, NIL)
  187. END;
  188. IF nofLevelsPerPage.Get() = 0 THEN RETURN END;
  189. ASSERT(nofLevelsPerPage.Get() > 0);
  190. IF (scrollPanel # NIL) THEN
  191. spw := scrollPanel.bounds.GetWidth(); sph := scrollPanel.bounds.GetHeight();
  192. tw := spw + scrollPanel.dx; th := sph + scrollPanel.dy;
  193. sw := vScrollbar.width.Get(); sh := hScrollbar.width.Get();
  194. w := bounds.GetWidth(); h := bounds.GetHeight();
  195. (* is hScrollbar visible ? *)
  196. IF (tw > w) OR ((th>h) & (tw>(w-sw))) THEN
  197. (* is vScrollbar visible ? *)
  198. IF (th > (h-sh)) OR (tw<=w) THEN rw := w - sw ELSE rw := w END;
  199. dx := tw- rw;
  200. hScrollbar.visible.Set(TRUE);
  201. IF rw > 0 THEN
  202. nofLevels := MAX(minNofLevels.Get(), nofLevelsPerPage.Get() * dx DIV rw);
  203. END;
  204. hScrollbar.max.Set(nofLevels);
  205. (* hScrollbar.pageSize.Set(MAX(1, (rw * nofLevels) DIV dx)); *)
  206. hScrollbar.pageSize.Set(MAX(1, (rw * nofLevels) DIV th) + 1);
  207. IF (sequencer # NIL) & sequencer.IsCallFromSequencer() THEN
  208. hScrollbar.RecacheProperties; (* workaround because, InternalPropertyChanged is InUpdate *)
  209. END;
  210. ELSE
  211. dx := 0;
  212. hScrollbar.visible.Set(FALSE);
  213. END;
  214. (* is vScrollbar visible ? *)
  215. IF (th > h) OR ((tw>w) & (th>(h-sh))) THEN
  216. (* is hScrollbar visible ? *)
  217. IF (tw > (w-sw)) OR (th<=h) THEN rh := h - sh ELSE rh := h END;
  218. dy := th - rh;
  219. vScrollbar.visible.Set(TRUE);
  220. IF rh > 0 THEN
  221. nofLevels := MAX(minNofLevels.Get(), nofLevelsPerPage.Get() * dy DIV rh)
  222. END;
  223. vScrollbar.max.Set(nofLevels);
  224. (* vScrollbar.pageSize.Set(MAX(1, (rh * nofLevels) DIV dy)); *)
  225. vScrollbar.pageSize.Set(MAX(1, (rh * nofLevels) DIV th) + 1);
  226. vScrollbar.RecacheProperties; (* workaround because, InternalPropertyChanged is InUpdate *)
  227. ELSE
  228. dy := 0;
  229. vScrollbar.visible.Set(FALSE);
  230. END
  231. END;
  232. IF ~aligning THEN AlignSubComponents END;
  233. Invalidate;
  234. END FitScrollTarget;
  235. PROCEDURE ScrollbarsChanged(sender, data : ANY);
  236. BEGIN
  237. scrollPanel.SetLeftTop(hScrollbar.pos.Get() / (hScrollbar.max.Get() - hScrollbar.min.Get()),
  238. vScrollbar.pos.Get() / (vScrollbar.max.Get() - vScrollbar.min.Get()));
  239. Invalidate
  240. END ScrollbarsChanged;
  241. PROCEDURE WheelMove*(dz : LONGINT);
  242. CONST Multiplier = 3;
  243. VAR pos : LONGINT;
  244. BEGIN
  245. WheelMove^(dz);
  246. IF vScrollbar.visible.Get() THEN
  247. pos := vScrollbar.pos.Get() + Multiplier * dz;
  248. IF pos < vScrollbar.min.Get() THEN pos := vScrollbar.min.Get(); END;
  249. IF pos > vScrollbar.max.Get() THEN pos := vScrollbar.max.Get(); END;
  250. vScrollbar.pos.Set(pos);
  251. ScrollbarsChanged(NIL, NIL);
  252. END;
  253. END WheelMove;
  254. PROCEDURE AddInternalComponent*(component : WMComponents.Component);
  255. BEGIN
  256. scrollPanel.AddInternalComponent(component);
  257. END AddInternalComponent;
  258. (** Iff data IS WMGraphics.Image, it is set as background. Else the background is set to white *)
  259. (* Note: Only use for anonymous Images without a specific Name *)
  260. PROCEDURE AddContent*(content : XML.Content);
  261. BEGIN
  262. IF (content IS WMProperties.Properties) OR (content = vScrollbar) OR (content = hScrollbar) OR (content = scrollPanel) THEN
  263. AddContent^(content);
  264. ELSE
  265. scrollPanel.AddContent(content);
  266. END;
  267. END AddContent;
  268. END ScrollableContainer;
  269. VAR
  270. Int32Prototype : WMProperties.Int32Property;
  271. (* Scrollable Container prototypes *)
  272. PrototypeSCMinNofLevels*, PrototypeSCNofLevelsPerPage*: WMProperties.Int32Property;
  273. PrototypeSCWheelScrolling : WMProperties.BooleanProperty;
  274. StrScrollPanel, StrScrollableContainer : String;
  275. PROCEDURE InitStrings;
  276. BEGIN
  277. StrScrollableContainer := Strings.NewString("ScrollableContainer");
  278. StrScrollPanel := Strings.NewString("ScrollPanel");
  279. END InitStrings;
  280. PROCEDURE InitPrototypes;
  281. VAR
  282. plScrollableContainer : WMProperties.PropertyList;
  283. BEGIN
  284. (* ScrollablePanel prototypes *)
  285. NEW(plScrollableContainer); WMComponents.propertyListList.Add("Scrollable Container", plScrollableContainer);
  286. NEW(Int32Prototype, NIL, NewString("MinNofLevels"), NewString("")); Int32Prototype.Set(8);
  287. NEW(PrototypeSCMinNofLevels, Int32Prototype, NIL, NIL); plScrollableContainer.Add(PrototypeSCMinNofLevels);
  288. NEW(Int32Prototype, NIL, NewString("NofLevelsPerPage"), NewString("")); Int32Prototype.Set(8);
  289. NEW(PrototypeSCNofLevelsPerPage, Int32Prototype, NIL, NIL); plScrollableContainer.Add(PrototypeSCNofLevelsPerPage);
  290. NEW(PrototypeSCWheelScrolling, NIL, NewString("WheelScrolling"), NewString("Mouse wheel scrolling?"));
  291. PrototypeSCWheelScrolling.Set(TRUE);
  292. WMComponents.propertyListList.UpdateStyle
  293. END InitPrototypes;
  294. PROCEDURE GenScrollableContainer*() : XML.Element;
  295. VAR scrollCont: ScrollableContainer;
  296. BEGIN NEW(scrollCont); RETURN scrollCont
  297. END GenScrollableContainer;
  298. PROCEDURE NewString(CONST x : ARRAY OF CHAR) : String;
  299. VAR t : String;
  300. BEGIN
  301. NEW(t, LEN(x)); COPY(x, t^); RETURN t
  302. END NewString;
  303. BEGIN
  304. InitStrings;
  305. InitPrototypes;
  306. END WMScrollableComponents.
  307. System.Free WMScrollableComponents~