Rune Serialize - Stabilization, Generation, major overhaul
[rune.git] / classes / gadgets / slider.d
1 #
2 # GADGETS/SLIDER.D      - Slider Gadget
3 #
4 # Here is typical code creating a vertical and horizontal slider for an
5 # area.  The main frame is required to be a window.  The inner frame (frame2)
6 # may or may not be a window.
7
8 #    sliderH.createSlider(frame, 0, 0, Frame.BELOW|Frame.FILLX);
9 #    sliderV.createSlider(frame, 0, 0, Frame.RIGHT|Frame.FILLY);
10 #    frame1.createFrame(frame, 0, 0, 
11 #                       Frame.BODY|Frame.FILLX|Frame.FILLY|Frame.WINDOW);
12 #    frame2.createFrame(frame1, 1000, 1000, Frame.BODY);
13 #    sliderH->setBody(frame1, frame2);
14 #    sliderV->setBody(frame1, frame2);
15
16 public subclass SliderFrame from Frame {
17         int holding;
18         int spos;               # slider position (x or y dep on expansionMode)
19         int slen;               # size of slider
20         int upos;               # start position in pixels
21         int umax;               # maximum range in pixels
22         Point lpos;             # last mouse event
23         Frame @obody;           # window on world
24         Frame @ibody;           # frame that needs to be scrolled
25         SliderFrame @other;     # the other slider, if any
26 }
27
28 public method
29 void
30 SliderFrame.createSlider(
31         lvalue SliderFrame @this,
32         Frame @parent,
33         int w,
34         int h,
35         SliderFrame @other,
36         int mount
37 ) {
38         # Minimum slider length in pixels
39         #
40         if (w <= 0)
41                 w = 16;
42         if (h <= 0)
43                 h = 16;
44
45         this.createFrame("slider", parent, w, h, mount);
46         if (other != NULL) {
47                 this->other = other;
48                 other->other = this;
49         }
50         this->refreshVisibleFrame();
51 }
52
53 refine public method 
54 int
55 SliderFrame.mouseMoved(Event @ev)
56 {
57         if (this.holding) {
58                 int rel;        # relative movement on correct axis
59                 int max;        # maximum movement on correct axis
60                 int adj;        # out of bounds re-adjustment
61
62                 switch(this.expansionMode & Frame.DIRMASK) {
63                 case Frame.LEFT:
64                 case Frame.RIGHT:
65                         #
66                         # vertical slider on the left or right
67                         #
68                         rel = ev->pos.y - this.lpos.y;
69                         max = this.bounds.h - 4;
70                         break;
71                 case Frame.ABOVE:
72                 case Frame.BELOW:
73                         #
74                         # horizontal slider on the top or bottom
75                         #
76                         rel = ev->pos.x - this.lpos.x;
77                         max = this.bounds.w - 4;
78                         break;
79                 }
80
81                 this.spos += rel;
82                 this.lpos = ev->pos;
83                 if (this.spos < 0) {
84                         adj = -this.spos;
85                 } else if (this.spos + this.slen > max) {
86                         adj = max - (this.spos + this.slen);
87                 }
88
89                 # keep the mouse lpos in sync so if we go out of bounds
90                 # and then move back the slider does not move until we
91                 # are back in bounds.
92                 #
93                 if (adj != 0) {
94                         this.spos += adj;
95                         switch(this.expansionMode & Frame.DIRMASK) {
96                         case Frame.LEFT:
97                         case Frame.RIGHT:
98                                 this.lpos.y += adj;
99                                 break;
100                         case Frame.ABOVE:
101                         case Frame.BELOW:
102                                 this.lpos.x += adj;
103                                 break;
104                         }
105                 }
106
107                 # If some movement occured calculate the user pixel position
108                 # change and slide the window for real
109                 #
110                 if (rel + adj != 0 && this.obody != NULL && max > 0) {
111                         int upos = this.spos * this.umax / max; 
112
113                         if (upos != this.upos) {
114                                 this.upos = upos;
115                                 switch(this.expansionMode & Frame.DIRMASK) {
116                                 case Frame.LEFT:
117                                 case Frame.RIGHT:
118                                         this.vpos.y = -upos;
119                                         break;
120                                 case Frame.ABOVE:
121                                 case Frame.BELOW:
122                                         this.vpos.x = -upos;
123                                         break;
124                                 }
125                                 if (this.other != NULL) {
126                                         this.other->vpos = this.vpos;
127                                 }
128                                 if (this.ibody->expansionMode & Frame.WINDOW) {
129                                         this.ibody->vpos = this.vpos;
130                                         this.ibody->moveWindow();
131                                 } else {
132                                         this.ibody->pos = this.vpos;
133                                         this.ibody->clearArea(
134                                                     -this.vpos.x,
135                                                     -this.vpos.y,
136                                                     this.obody->bounds.w,
137                                                     this.obody->bounds.h);
138                                         # yuch. this is due to our wpos
139                                         # optimization.  We need a way to
140                                         # regenerate the optimization on
141                                         # the fly to avoid having to recalc()
142                                         #
143                                         # XXX pos not good enough, clip area
144                                         # must also be cleaned up.
145                                         #
146                                         this.ibody->recalc();
147                                 }
148                                 this.refreshVisibleFrame();
149                         }
150                 }
151         }
152         return(0);
153 }
154
155 method void
156 SliderFrame.refreshBulk()
157 {
158         if (this.ibody->expansionMode & Frame.WINDOW) {
159                 Frame @body = this.ibody;
160
161                 this.ibody->moveWindow();
162         } else {
163                 # XXX recalculate clip area
164                 this.ibody->pos = this.vpos;
165                 this.ibody->clearArea(-this.vpos.x, -this.vpos.y,
166                                       this.obody->bounds.w,
167                                       this.obody->bounds.h);
168                 this.ibody->recalc();
169         }
170 }
171
172 refine public method 
173 int
174 SliderFrame.buttonPressed(Event @ev)
175 {
176         switch(ev->keySym) {
177         case Event.XK_Pointer_Button1:
178                 if (this.mouseInBounds(ev)) {
179                         this.holding = ev->keySym;
180                         this.lpos = ev->pos;
181                         this.setMouseFocus();
182                 }
183                 this.refreshVisibleFrame();
184                 break;
185         case -Event.XK_Pointer_Button1:
186                 if (this.holding != 0) {
187                         this.holding = 0;
188                         this.refreshVisibleFrame();
189                         this.clearMouseFocus();
190                 }
191                 break;
192         default:
193                 break;
194         }
195         return(0);
196 }
197
198 /*
199  * CALCULATEPOSITIONS()
200  *
201  *      Intercept the recalculation sequence to adjust our parameters.
202  *      This occurs when the windows are created or resized.
203  */
204 refine public method
205 void
206 SliderFrame.frameGeometryChanged()
207 {
208         super.frameGeometryChanged();
209         if (this.ibody != NULL && this.obody != NULL)
210                 this.setBody(this.obody, this.ibody);
211 }
212
213 public method
214 void
215 SliderFrame.setBody(Frame @obody, Frame @ibody)
216 {
217         this.obody = obody;
218         this.ibody = ibody;
219
220         switch(this.expansionMode & Frame.DIRMASK) {
221         case Frame.LEFT:
222         case Frame.RIGHT:
223                 this.setBulk(ibody->bounds.h, doRefresh:0);
224                 break;
225         case Frame.ABOVE:
226         case Frame.BELOW:
227                 this.setBulk(ibody->bounds.w, doRefresh:0);
228                 break;
229         }
230 }
231
232 public method
233 int
234 SliderFrame.setBulk(int umax, int doRefresh = 1)
235 {
236         int max;
237         int bmax;
238         int adj = 0;
239
240         switch(this.expansionMode & Frame.DIRMASK) {
241         case Frame.LEFT:
242         case Frame.RIGHT:
243                 max = this.bounds.h - 4;
244                 if (this.obody != NULL)
245                         bmax = this.obody->bounds.h;
246                 else
247                         bmax = this.bounds.h;
248                 break;
249         case Frame.ABOVE:
250         case Frame.BELOW:
251                 max = this.bounds.w - 4;
252                 if (this.obody != NULL)
253                         bmax = this.obody->bounds.w;
254                 else
255                         bmax = this.bounds.w;
256                 break;
257         }
258         if (umax > 0) {
259                 if (this.umax != umax) {
260                         this.spos = this.upos * max / umax;
261                         this.umax = umax;
262                 }
263                 this.slen = (max * bmax) / umax;
264                 if (this.spos + this.slen > max) {
265                         adj = max - (this.spos + this.slen);
266                         this.spos += adj;
267                 }
268                 if (doRefresh)
269                         this.refreshVisibleFrame();
270         }
271         return(adj);
272 }
273
274 refine public method
275 bool
276 SliderFrame.refreshFrame(int x, int y, int w, int h)
277 {
278         Pen @pen1;
279         Pen @pen2;
280         int descent;
281         int w;
282         int h;
283
284         w = this.bounds.w;
285         h = this.bounds.h;
286
287         if (this.umax > 0)
288                 this.setBulk(this.umax, doRefresh : 0);
289         this.pen = this.mediumPen;
290         this.fillRect(0, 0, this.bounds.w, this.bounds.h);
291         this.drawShadowedBorder(0, 0, w, h, this.darkPen, this.lightPen);
292
293         if (this.holding == Event.XK_Pointer_Button1) {
294                 pen1 = this.darkPen;
295                 pen2 = this.lightPen;
296         } else {
297                 pen1 = this.lightPen;
298                 pen2 = this.darkPen;
299         }
300         switch(this.expansionMode & Frame.DIRMASK) {
301         case Frame.LEFT:
302         case Frame.RIGHT:
303                 this.drawShadowedBorder(2, this.spos + 2, w - 4,
304                                         this.slen, pen1, pen2);
305                 this.pen = this.textPen;
306                 this.fillRect(4, this.spos + 4, w - 8, this.slen - 4);
307                 break;
308         case Frame.ABOVE:
309         case Frame.BELOW:
310                 this.drawShadowedBorder(this.spos + 2, 2,
311                                         this.slen, h - 4, pen1, pen2);
312                 this.pen = this.textPen;
313                 this.fillRect(this.spos + 4, 4, this.slen - 4, h - 8);
314                 break;
315         }
316 #    if (this.ibody != NULL) {
317 #       Frame @body = this.ibody;
318 #       if (body->expansionMode & Frame.CLIP) {
319 #           body->expansionMode &= ~Frame.CLIP;
320 #           this.refreshBulk();
321 #       }
322 #    }
323         return(0);
324 }