1 namespace gui::controls;
5 public enum TabsPlacement
7 top, bottom, left, right
10 class TabButton : Button
18 bool OnKeyHit(Key key, unichar ch)
23 bool OnLeftButtonDown(int x, int y, Modifiers mods)
25 Button::OnLeftButtonDown(x, y, mods);
26 Button::OnLeftButtonUp(x, y, mods);
30 void OnRedraw(Surface surface)
32 ColorKey keys[2] = { { silver, 0.0f }, { white, 1.0f } };
33 ColorKey rKeys[2] = { { white, 0.0f }, { silver, 1.0f } };
37 int w = clientSize.w, h = clientSize.h;
38 bool checked = this.checked;
39 ButtonState state = buttonState;
40 if(state == down) state = over;
42 surface.SetForeground(gray);
47 switch(((TabControl)parent.parent).placement)
51 surface.DrawLine(x,0, x,h-3);
52 surface.DrawLine(w-2,0, w-2,h-3);
56 surface.DrawLine(x,2, x,h-1);
57 surface.DrawLine(w-2,2, w-2,h-1);
61 surface.DrawLine(0,y, w-3,y);
62 surface.DrawLine(0,h-2, w-3,h-2);
66 surface.DrawLine(2,y, w-1,y);
67 surface.DrawLine(2,h-2, w-1,h-2);
74 keys[0].color = lightGray;
75 rKeys[1].color = lightGray;
77 switch(((TabControl)parent.parent).placement)
79 case bottom: surface.Gradient(keys, sizeof(keys) / sizeof(ColorKey), 1, vertical, x+1, 0, w-3, h - 3); break;
80 case top: surface.Gradient(rKeys, sizeof(rKeys) / sizeof(ColorKey), 1, vertical, x+1, 2, w-3, h - 1); break;
82 case right: surface.Gradient(keys, sizeof(keys) / sizeof(ColorKey), 1, horizontal, 0, y+1, w-3, h - 3); break;
83 case left: surface.Gradient(rKeys, sizeof(rKeys) / sizeof(ColorKey), 1, horizontal, 2, y+1, w-1, h - 3); break;
88 surface.SetBackground(background /*white*/);
89 switch(((TabControl)parent.parent).placement)
91 case bottom: surface.Area(x+1, 0, w-3, h - 3); break;
92 case top: surface.Area(x+1, 2, w-3, h - 1); surface.foreground = white; surface.VLine(2, h - 1, x+1); break;
93 case right: surface.Area(0, y+1, w-3, h - 3); break;
94 case left: surface.Area(2, y+1, w-1, h - 3); break;
98 if(state == over || checked)
99 surface.SetForeground(lightBlue);
101 surface.SetForeground(white);
103 switch(((TabControl)parent.parent).placement)
106 surface.DrawLine(x+2,h-2, w-4, h-2);
107 surface.DrawLine(x+1, h-3, w-3, h-3);
110 surface.DrawLine(x+2,1, w-4, 1);
111 surface.DrawLine(x+1,2, w-3, 2);
114 surface.DrawLine(w-2, y+2, w-2, h-4);
115 surface.DrawLine(w-3, y+1, w-3, h-3);
118 surface.DrawLine(1, y+2, 1, h-4);
119 surface.DrawLine(2, y+1, 2, h-3);
122 if(state == over || checked)
123 surface.SetForeground(slateBlue);
125 surface.SetForeground(gray);
127 switch(((TabControl)parent.parent).placement)
130 surface.DrawLine(x+2,h-1, w-4, h-1);
131 surface.PutPixel(x,h-3);
132 surface.PutPixel(x+1,h-2);
134 surface.PutPixel(w-2, h - 3);
135 surface.PutPixel(w-3, h - 2);
138 surface.DrawLine(x+2,0, w-4,0);
139 surface.PutPixel(x, 2);
140 surface.PutPixel(x+1, 1);
142 surface.PutPixel(w-2, 2);
143 surface.PutPixel(w-3, 1);
146 surface.DrawLine(w-1,y+2, w-1, h-4);
147 surface.PutPixel(w-3, y);
148 surface.PutPixel(w-2, y+1);
150 surface.PutPixel(w - 3, h-2);
151 surface.PutPixel(w - 2, h-3);
154 surface.DrawLine(0,y+2, 0, h-4);
155 surface.PutPixel(2, y);
156 surface.PutPixel(1, y+1);
158 surface.PutPixel(2, h-2);
159 surface.PutPixel(1, h-3);
163 surface.SetForeground(foreground);
165 surface.TextExtent(text, strlen(text),&tw, &th);
166 y = (clientSize.h - th - 1)/2 + (checked ? 0 : -2);
170 int width = clientSize.w - 2*6;
173 surface.WriteTextDots(alignment, x, y, width, text, strlen(text));
177 int width = clientSize.w - 2 * 6;
182 if(alignment == right)
184 else if(alignment == center)
185 x += (width - tw) / 2;
187 WriteCaption(surface, x, y);
190 // Draw Active Stipple
195 int x2 = clientSize.w - 6;
196 int y2 = clientSize.h - 5;
197 if((x2 - x1) & 1) x2++;
198 if((y2 - y1) & 1) y2++;
200 surface.LineStipple(0x5555);
201 surface.Rectangle(x1, y1, x2, y2);
202 surface.LineStipple(0);
207 static void PlaceButton(TabButton button, TabsPlacement placement, bool selected, int buttonsOffset)
209 int of = (selected) ? 0 : 1;
213 button.size = selected ? { 74, 25 } : { 70, 22 };
214 button.anchor = Anchor { left = buttonsOffset + (int)button.tab.id * 70 + 2*of, bottom = 0 };
217 button.size = selected ? { 74, 25 } : { 70, 22 };
218 button.anchor = Anchor { left = buttonsOffset + (int)button.tab.id * 70 + 2*of, top = 0 };
221 button.size = selected ? { 73, 26 } : { 70, 22 };
222 button.anchor = Anchor { top = buttonsOffset + (int)button.tab.id * 22 + 2*of, right = 0 };
225 button.size = selected ? { 73, 26 } : { 70, 22 };
226 button.anchor = Anchor { top = buttonsOffset + (int)button.tab.id * 22 + 2*of, left = 0 };
231 static define skinMainColor = Color { 0, 71, 128 };
232 static define skinBackground = Color { 255, 255, 255 };
233 static define skinTextColor = skinMainColor;
234 static define skinInactiveTextColor = Color { 0, 51, 108 };
237 #define NAME_OFFSET 2
238 #define NAME_OFFSETX 4
241 #define MENU_HEIGHT 25
242 #define STATUS_HEIGHT 18
244 public class TabControl : Window
246 TabsPlacement placement;
251 background = white;//formColor;
253 Window tabButtons { this, opacity = 0, drawBehind = true };
260 public property TabsPlacement placement { set { placement = value; } get { return placement; } }
261 public property int buttonsOffset { set { buttonsOffset = value; } get { return buttonsOffset; } }
262 public property Tab curTab
270 // value.button.Activate();
271 value.button.MakeActive();
272 value.button.NotifyClicked(value.button.master, value.button, 0,0, 0);
276 get { return curTab; }
279 bool IsInside(int x, int y)
281 if(Window::IsInside(x, y))
283 Point tbAbsPos = tabButtons.absPosition;
284 x += absPosition.x - tbAbsPos.x;
285 y += absPosition.y - tbAbsPos.y;
287 if(tabButtons.IsInside(x, y))
290 for(button = tabButtons.firstChild; button; button = button.next)
292 int bx = x + tbAbsPos.x - button.absPosition.x;
293 int by = y + tbAbsPos.y - button.absPosition.y;
294 if(button.IsInside(bx, by))
304 void ShowDecorations(Font captionFont, Surface surface, const char * name, bool active, bool moving)
306 if(placement == bottom && ((BorderBits)borderStyle).fixed)
308 int top = 0, border = 0, bottom = 0;
309 if(state == minimized)
310 top = border = bottom = 0;
311 else if(((BorderBits)borderStyle).sizable)
317 else if(((BorderBits)borderStyle).fixed)
324 if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
326 surface.SetForeground(gray);
327 surface.Rectangle(0,1, size.w-1, CAPTION+1);
329 surface.SetForeground(white);
330 surface.Rectangle(1, 1, clientSize.w-2, CAPTION-1);
332 surface.SetBackground(skinBackground);
333 surface.Area(2, 2, size.w-3, CAPTION+1);
335 surface.SetForeground((active ? skinTextColor : skinInactiveTextColor));
336 surface.TextOpacity(false);
337 surface.TextFont(captionFont);
340 int buttonsSize = border +
341 ((hasMaximize || hasMinimize) ? 52 : 18);
342 surface.WriteTextDots(left, border + NAME_OFFSETX, top + NAME_OFFSET,
343 size.w - (buttonsSize + border + 4), name, strlen(name));
347 if(state != minimized && hasHorzScroll && hasVertScroll)
349 if(sbh && sbh.visible && sbv && sbv.visible)
351 surface.SetBackground(formColor);
353 clientStart.x + clientSize.w,
354 clientStart.y + clientSize.h,
355 clientStart.x + clientSize.w + SB_WIDTH - 1,
356 clientStart.y + clientSize.h + SB_HEIGHT - 1);
362 Window::ShowDecorations(captionFont, surface, name, active, moving);
366 surface.SetForeground(white);
368 clientStart.x + 1 + (placement == left) * 80,
369 clientStart.y + 1 + (placement == top) * 30,
370 clientStart.x + clientSize.w - (placement == right) * 80 - 2,
371 clientStart.y + clientSize.h - (placement == bottom) * 30 - 2);
372 surface.SetForeground(gray);
374 clientStart.x + (placement == left) * 80,
375 clientStart.y + (placement == top) * 30,
376 clientStart.x + clientSize.w - (placement == right) * 80 - 1,
377 clientStart.y + clientSize.h - (placement == bottom) * 30 - 1);
383 //int id = curTab ? (int)curTab.id : 0;
384 Button button = curButton;
385 int x = button.position.x;
386 int y = button.position.y;
390 case TabsPlacement::bottom:
391 box = { /*((id == 0) ? 0 : */x/*)*/ + 1 + clientStart.x, clientSize.h-32 + clientStart.y + 1, x + clientStart.x + button.size.w - 2, clientSize.h-28 + clientStart.y + 1 };
393 case TabsPlacement::top:
394 box = { /*((id == 0) ? 0 : */x/*)*/ + 1 + clientStart.x, clientStart.y + 30, x + clientStart.x + button.size.w - 2, clientStart.y + 31 };
396 case TabsPlacement::left:
397 box = { 78 + clientStart.x, /*((id == 0) ? 0 : */y/*)*/ + 1 + clientStart.y, 81 + clientStart.x, y + button.size.h + clientStart.y - 2 };
399 case TabsPlacement::right:
400 box = { clientSize.w - 80, /*((id == 0) ? 0 : */y/*)*/ + 1 + clientStart.y, clientSize.w - 78 + clientStart.x + 1, y + clientStart.y + button.size.h - 2 };
404 surface.SetBackground(background /*white*/);
405 surface.Clear(colorBuffer);
408 surface.SetForeground(white);
411 case TabsPlacement::bottom: surface.VLine(clientSize.h-32 + clientStart.y + 1, clientSize.h-28 + clientStart.y + 1, x + 1 + clientStart.x); break;
412 case TabsPlacement::top: surface.VLine(clientStart.y + 30, clientStart.y + 31, x + 1 + clientStart.x); break;
413 case TabsPlacement::left: surface.HLine(78 + clientStart.x, 81 + clientStart.x, y + 1 + clientStart.y); break;
414 case TabsPlacement::right: surface.HLine(clientStart.y + 30, clientStart.y + 31, y + 1 + clientStart.y); break;
419 void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
421 if(placement == bottom && ((BorderBits)borderStyle).fixed)
424 if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
426 if(hasMenuBar && state != minimized)
428 if(statusBar && state != minimized)
432 Window::GetDecorationsSize(w, h);
435 bool IsMouseResizing(int x, int y, int w, int h, bool *resizeX, bool *resizeY, bool *resizeEndX, bool *resizeEndY)
437 if(placement == bottom && ((BorderBits)borderStyle).fixed)
441 *resizeX = *resizeY = *resizeEndX = *resizeEndY = false;
443 if(((BorderBits)borderStyle).sizable && (state == normal))
446 if(Box { 0, 0,2, 2 }.IsPointInside({x, y}))
447 result = *resizeX = *resizeY = true;
449 if(Box { w-2, 0, w-1, 2 }.IsPointInside({x, y}))
450 result = *resizeEndX = *resizeY = true;
452 if(Box { 0, h-2, 1, h-1 }.IsPointInside({x, y}))
453 result = *resizeX = *resizeEndY = true;
454 // BottomRight Corner
455 if(Box { w-2, h-32, w-1, h-25 }.IsPointInside({x, y}))
456 result = *resizeEndX = *resizeEndY = true;
459 if(Box { 0,0, 1, h-1 }.IsPointInside({x, y}))
460 result = *resizeX = true;
462 if(Box { w-1, 0, w-2, h-1 }.IsPointInside({x, y}))
463 result = *resizeEndX = true;
465 if(Box { 0, 0, w-1, 2 }.IsPointInside({x, y}))
466 result = *resizeY = true;
468 if(Box { numTabs * 70, h-32, w-2, h-25 }.IsPointInside({x, y}))
469 result = *resizeEndY = true;
474 return Window::IsMouseResizing(x, y, w, h, resizeX, resizeY, resizeEndX, resizeEndY);
477 void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
479 if(placement == bottom && ((BorderBits)borderStyle).fixed)
481 MinMaxValue aw = 0, ah = 0;
485 GetDecorationsSize(&aw, &ah);
490 if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
493 if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
499 // Reduce client area
507 Window::SetWindowArea(x, y, w, h, cw, ch);
515 tabButtons.anchor = { top = 5, left = 0, right = 5 };
516 tabButtons.size = { h = 25 };
519 tabButtons.anchor = { bottom = 5, left = 0, right = 5 };
520 tabButtons.size = { h = 25 };
523 tabButtons.anchor = { top = 0, left = 5, bottom = 5 };
524 tabButtons.size = { w = 75 };
527 tabButtons.anchor = { top = 0, bottom = 5, right = 5 };
528 tabButtons.size = { w = 75 };
536 // curButton.Activate();
537 curButton.MakeActive();
541 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
543 if(curTab == (Tab)(intptr)button.id)
545 //curButton.Activate();
546 curButton.MakeActive();
548 if(curTab.Destroy(0))
550 curButton.checked = false;
551 button.checked = true;
552 curTab = (Tab)(intptr)button.id;
555 PlaceButton(curButton, placement, false, buttonsOffset);
557 curButton = (TabButton)button;
558 curButton.MakeActive();
559 // curButton.Activate();
561 PlaceButton(curButton, placement, true, buttonsOffset);
573 public void AddTab(Tab tab)
575 if(tab.parent != this)
580 tab.autoCreate = false;
584 tab.button = TabButton
587 master = this, stayDown = true,
588 text = tab.text, id = (int64)(intptr)tab, NotifyClicked = NotifyClicked,
590 background = background;
605 curButton = tab.button;
606 tab.button.checked = true;
607 tab.autoCreate = true;
610 PlaceButton(tab.button, placement, curTab == tab, buttonsOffset);
613 case top: tab.anchor = { left = 2, bottom = 2, right = 2, top = 32 }; break;
614 case bottom: tab.anchor = { left = 2, bottom = 32, right = 2, top = 2 }; break;
615 case left: tab.anchor = { left = 82, bottom = 2, right = 2, top = 2 }; break;
616 case right: tab.anchor = { left = 2, bottom = 2, right = 82, top = 2 }; break;
620 public void RemoveTab(Tab tab)
623 Tab fallbackTab = null;
624 // tab.parent = null;
625 for(child = tabButtons.children.first; child; child = child.next)
627 if(child._class == class(TabButton))
629 TabButton button = (TabButton)child;
630 if(button.id == (int64)(intptr)tab)
640 fallbackTab = button.tab;
646 fallbackTab = tabButtons.children.first ? ((TabButton)tabButtons.children.first).tab : null;
648 fallbackTab.SelectTab();
651 /*curTab = fallbackTab;
652 curButton = curTab.button;
653 curButton.checked = true;
654 curTab.autoCreate = true;*/
661 for(child = tabButtons.firstChild; child; child = next)
667 for(child = firstChild; child; child = next)
670 if(eClass_IsDerived(child._class, class(Tab)))
675 bool OnKeyHit(Key key, unichar ch)
677 SmartKey smartKey = (SmartKey)key;
678 if(tabButtons.active && (smartKey == left || smartKey == right))
680 // BIG MESS... USE AN ARRAY INSTEAD?
681 TabButton newButton = null, button;
682 int cleft = curButton.anchor.left.distance, ctop = curButton.anchor.top.distance;
684 for(button = (TabButton)tabButtons.firstChild; button; button = (TabButton)button.next)
686 int pleft = button.anchor.left.distance, nleft = newButton ? newButton.anchor.left.distance : 0;
687 int ptop = button.anchor.top.distance, ntop = newButton ? newButton.anchor.top.distance : 0;
688 if(button == curButton) continue;
689 if((smartKey == left && (pleft < cleft || ptop < ctop) && (!newButton || pleft > nleft || ptop > ntop)) ||
690 (smartKey == right && (pleft > cleft || ptop > ctop) && (!newButton || pleft < nleft || pleft < ptop)))
695 for(button = (TabButton)tabButtons.firstChild; button; button = (TabButton)button.next)
697 int pleft = button.anchor.left.distance, nleft = newButton ? newButton.anchor.left.distance : 0;
698 int ptop = button.anchor.top.distance, ntop = newButton ? newButton.anchor.top.distance : 0;
699 if(button == curButton) continue;
700 if(!newButton || (smartKey == right && (pleft < nleft || ptop < ntop)) || (smartKey == left && (pleft > nleft || ptop > ntop)))
707 property::curTab = newButton.tab;
708 tabButtons.MakeActive();
709 // tabButtons.Activate();
717 public class Tab : Window
720 TabControl tabControl;
722 //borderStyle = contour;
723 //background = lightBlue;
728 if(parent && eClass_IsDerived(parent._class, class(TabControl)))
730 tabControl = (TabControl)parent;
731 tabControl.AddTab(this);
733 else if(!parent && tabControl)
735 tabControl.RemoveTab(this);
740 public property TabControl tabControl
742 set { parent = value; }
743 get { return (TabControl)parent; }
746 public void SelectTab()
748 button.NotifyClicked(button.master, button, 0, 0, 0);