1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: univ/renderer.cpp
3 // Purpose: wxControlRenderer implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "renderer.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
33 #include "wx/control.h"
34 #include "wx/checklst.h"
35 #include "wx/listbox.h"
36 #include "wx/scrolbar.h"
43 #include "wx/univ/theme.h"
44 #include "wx/univ/renderer.h"
45 #include "wx/univ/colschem.h"
51 // ============================================================================
53 // ============================================================================
55 // ----------------------------------------------------------------------------
56 // wxRenderer: drawing helpers
57 // ----------------------------------------------------------------------------
59 void wxRenderer::StandardDrawFrame(wxDC& dc,
60 const wxRect& rectFrame,
61 const wxRect& rectLabel)
63 // draw left, bottom and right lines entirely
64 DrawVerticalLine(dc, rectFrame.GetLeft(),
65 rectFrame.GetTop(), rectFrame.GetBottom() - 2);
66 DrawHorizontalLine(dc, rectFrame.GetBottom() - 1,
67 rectFrame.GetLeft(), rectFrame.GetRight());
68 DrawVerticalLine(dc, rectFrame.GetRight() - 1,
69 rectFrame.GetTop(), rectFrame.GetBottom() - 1);
71 // and 2 parts of the top line
72 DrawHorizontalLine(dc, rectFrame.GetTop(),
73 rectFrame.GetLeft() + 1, rectLabel.GetLeft());
74 DrawHorizontalLine(dc, rectFrame.GetTop(),
75 rectLabel.GetRight(), rectFrame.GetRight() - 2);
79 void wxRenderer::StandardDrawTextLine(wxDC& dc,
82 int selStart, int selEnd,
85 if ( (selStart == -1) || !(flags & wxCONTROL_FOCUSED) )
88 dc.DrawText(text, rect.x, rect.y);
90 else // we have selection
95 // draw the part before selection
96 wxString s(text, (size_t)selStart);
99 dc.DrawText(s, x, rect.y);
101 dc.GetTextExtent(s, &width, NULL);
105 // draw the selection itself
106 s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
109 wxColour colFg = dc.GetTextForeground(),
110 colBg = dc.GetTextBackground();
111 dc.SetTextForeground(wxTHEME_COLOUR(HIGHLIGHT_TEXT));
112 dc.SetTextBackground(wxTHEME_COLOUR(HIGHLIGHT));
113 dc.SetBackgroundMode(wxSOLID);
115 dc.DrawText(s, x, rect.y);
116 dc.GetTextExtent(s, &width, NULL);
119 dc.SetBackgroundMode(wxTRANSPARENT);
120 dc.SetTextBackground(colBg);
121 dc.SetTextForeground(colFg);
124 // draw the final part
125 s = text.c_str() + selEnd;
128 dc.DrawText(s, x, rect.y);
133 // ----------------------------------------------------------------------------
134 // wxRenderer: scrollbar geometry
135 // ----------------------------------------------------------------------------
138 void wxRenderer::StandardScrollBarThumbSize(wxCoord length,
145 // the thumb can't be made less than this number of pixels
146 static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable
148 *thumbStart = (length*thumbPos) / range;
149 *thumbEnd = (length*(thumbPos + thumbSize)) / range;
151 if ( *thumbEnd - *thumbStart < thumbMinWidth )
153 // adjust the end if possible
154 if ( *thumbStart <= length - thumbMinWidth )
156 // yes, just make it wider
157 *thumbEnd = *thumbStart + thumbMinWidth;
159 else // it is at the bottom of the scrollbar
161 // so move it a bit up
162 *thumbStart = length - thumbMinWidth;
169 wxRect wxRenderer::StandardGetScrollbarRect(const wxScrollBar *scrollbar,
170 wxScrollBar::Element elem,
172 const wxSize& sizeArrow)
174 if ( thumbPos == -1 )
176 thumbPos = scrollbar->GetThumbPosition();
179 wxSize sizeTotal = scrollbar->GetClientSize();
180 wxCoord *start, *width, length, arrow;
182 if ( scrollbar->IsVertical() )
185 rect.width = sizeTotal.x;
186 length = sizeTotal.y;
188 width = &rect.height;
194 rect.height = sizeTotal.y;
195 length = sizeTotal.x;
203 case wxScrollBar::Element_Arrow_Line_1:
208 case wxScrollBar::Element_Arrow_Line_2:
209 *start = length - arrow;
213 case wxScrollBar::Element_Arrow_Page_1:
214 case wxScrollBar::Element_Arrow_Page_2:
215 // we don't have them at all
218 case wxScrollBar::Element_Thumb:
219 case wxScrollBar::Element_Bar_1:
220 case wxScrollBar::Element_Bar_2:
221 // we need to calculate the thumb position - do it
224 wxCoord thumbStart, thumbEnd;
225 int range = scrollbar->GetRange();
233 StandardScrollBarThumbSize(length,
235 scrollbar->GetThumbSize(),
241 if ( elem == wxScrollBar::Element_Thumb )
244 *width = thumbEnd - thumbStart;
246 else if ( elem == wxScrollBar::Element_Bar_1 )
251 else // elem == wxScrollBar::Element_Bar_2
254 *width = length - thumbEnd;
257 // everything is relative to the start of the shaft so far
262 case wxScrollBar::Element_Max:
264 wxFAIL_MSG( _T("unknown scrollbar element") );
271 wxCoord wxRenderer::StandardScrollBarSize(const wxScrollBar *scrollbar,
272 const wxSize& sizeArrowSB)
274 wxCoord sizeArrow, sizeTotal;
275 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
277 sizeArrow = sizeArrowSB.y;
278 sizeTotal = scrollbar->GetSize().y;
282 sizeArrow = sizeArrowSB.x;
283 sizeTotal = scrollbar->GetSize().x;
286 return sizeTotal - 2*sizeArrow;
290 wxCoord wxRenderer::StandardScrollbarToPixel(const wxScrollBar *scrollbar,
292 const wxSize& sizeArrow)
294 int range = scrollbar->GetRange();
297 // the only valid position anyhow
301 if ( thumbPos == -1 )
303 // by default use the current thumb position
304 thumbPos = scrollbar->GetThumbPosition();
307 return ( thumbPos*StandardScrollBarSize(scrollbar, sizeArrow) ) / range
308 + (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x);
312 int wxRenderer::StandardPixelToScrollbar(const wxScrollBar *scrollbar,
314 const wxSize& sizeArrow)
316 return ( (coord - (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x)) *
317 scrollbar->GetRange() ) /
318 StandardScrollBarSize(scrollbar, sizeArrow);
322 wxHitTest wxRenderer::StandardHitTestScrollbar(const wxScrollBar *scrollbar,
324 const wxSize& sizeArrowSB)
326 // we only need to work with either x or y coord depending on the
327 // orientation, choose one (but still check the other one to verify if the
328 // mouse is in the window at all)
329 wxCoord coord, sizeArrow, sizeTotal;
330 wxSize size = scrollbar->GetSize();
331 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
333 if ( pt.x < 0 || pt.x > size.x )
337 sizeArrow = sizeArrowSB.y;
342 if ( pt.y < 0 || pt.y > size.y )
346 sizeArrow = sizeArrowSB.x;
350 // test for the arrows first as it's faster
351 if ( coord < 0 || coord > sizeTotal )
355 else if ( coord < sizeArrow )
357 return wxHT_SCROLLBAR_ARROW_LINE_1;
359 else if ( coord > sizeTotal - sizeArrow )
361 return wxHT_SCROLLBAR_ARROW_LINE_2;
365 // calculate the thumb position in pixels
366 sizeTotal -= 2*sizeArrow;
367 wxCoord thumbStart, thumbEnd;
368 int range = scrollbar->GetRange();
371 // clicking the scrollbar without range has no effect
376 StandardScrollBarThumbSize(sizeTotal,
377 scrollbar->GetThumbPosition(),
378 scrollbar->GetThumbSize(),
384 // now compare with the thumb position
386 if ( coord < thumbStart )
387 return wxHT_SCROLLBAR_BAR_1;
388 else if ( coord > thumbEnd )
389 return wxHT_SCROLLBAR_BAR_2;
391 return wxHT_SCROLLBAR_THUMB;
395 wxRenderer::~wxRenderer()
399 // ----------------------------------------------------------------------------
401 // ----------------------------------------------------------------------------
403 wxControlRenderer::wxControlRenderer(wxWindow *window,
405 wxRenderer *renderer)
409 m_renderer = renderer;
411 wxSize size = m_window->GetClientSize();
414 m_rect.width = size.x;
415 m_rect.height = size.y;
418 void wxControlRenderer::DrawLabel(const wxBitmap& bitmap,
419 wxCoord marginX, wxCoord marginY)
421 m_dc.SetBackgroundMode(wxTRANSPARENT);
422 m_dc.SetFont(m_window->GetFont());
423 m_dc.SetTextForeground(m_window->GetForegroundColour());
425 wxString label = m_window->GetLabel();
426 if ( !label.empty() || bitmap.Ok() )
428 wxRect rectLabel = m_rect;
431 rectLabel.Inflate(-marginX, -marginY);
434 wxControl *ctrl = wxStaticCast(m_window, wxControl);
436 m_renderer->DrawButtonLabel(m_dc,
440 m_window->GetStateFlags(),
441 ctrl->GetAlignment(),
442 ctrl->GetAccelIndex());
446 void wxControlRenderer::DrawFrame()
448 m_dc.SetFont(m_window->GetFont());
449 m_dc.SetTextForeground(m_window->GetForegroundColour());
450 m_dc.SetTextBackground(m_window->GetBackgroundColour());
452 wxControl *ctrl = wxStaticCast(m_window, wxControl);
454 m_renderer->DrawFrame(m_dc,
455 m_window->GetLabel(),
457 m_window->GetStateFlags(),
458 ctrl->GetAlignment(),
459 ctrl->GetAccelIndex());
462 void wxControlRenderer::DrawButtonBorder()
464 int flags = m_window->GetStateFlags();
466 m_renderer->DrawButtonBorder(m_dc, m_rect, flags, &m_rect);
469 // m_renderer->DrawButtonSurface(m_dc, wxTHEME_BG_COLOUR(m_window), m_rect, flags );
472 void wxControlRenderer::DrawBitmap(const wxBitmap& bitmap)
474 int style = m_window->GetWindowStyle();
475 DrawBitmap(m_dc, bitmap, m_rect,
476 style & wxALIGN_MASK,
477 style & wxBI_EXPAND ? wxEXPAND : wxSTRETCH_NOT);
481 void wxControlRenderer::DrawBitmap(wxDC &dc,
482 const wxBitmap& bitmap,
487 // we may change the bitmap if we stretch it
488 wxBitmap bmp = bitmap;
492 int width = bmp.GetWidth(),
493 height = bmp.GetHeight();
497 if ( stretch & wxTILE )
500 for ( ; x < rect.width; x += width )
502 for ( y = 0; y < rect.height; y += height )
504 // no need to use mask here as we cover the entire window area
505 dc.DrawBitmap(bmp, x, y);
509 else if ( stretch & wxEXPAND )
511 // stretch bitmap to fill the entire control
512 bmp = wxBitmap(wxImage(bmp.ConvertToImage()).Scale(rect.width, rect.height));
514 else // not stretched, not tiled
516 if ( alignment & wxALIGN_RIGHT )
518 x = rect.GetRight() - width;
520 else if ( alignment & wxALIGN_CENTRE )
522 x = (rect.GetLeft() + rect.GetRight() - width + 1) / 2;
524 else // alignment & wxALIGN_LEFT
529 if ( alignment & wxALIGN_BOTTOM )
531 y = rect.GetBottom() - height;
533 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
535 y = (rect.GetTop() + rect.GetBottom() - height + 1) / 2;
537 else // alignment & wxALIGN_TOP
544 dc.DrawBitmap(bmp, x, y, TRUE /* use mask */);
547 void wxControlRenderer::DrawScrollbar(const wxScrollBar *scrollbar,
550 // we will only redraw the parts which must be redrawn and not everything
551 wxRegion rgnUpdate = scrollbar->GetUpdateRegion();
554 wxRect rectUpdate = rgnUpdate.GetBox();
555 wxLogTrace(_T("scrollbar"),
556 _T("%s redraw: update box is (%d, %d)-(%d, %d)"),
557 scrollbar->IsVertical() ? _T("vert") : _T("horz"),
558 rectUpdate.GetLeft(),
560 rectUpdate.GetRight(),
561 rectUpdate.GetBottom());
563 #if 0 //def WXDEBUG_SCROLLBAR
564 static bool s_refreshDebug = FALSE;
565 if ( s_refreshDebug )
567 wxClientDC dc(wxConstCast(scrollbar, wxScrollBar));
568 dc.SetBrush(*wxRED_BRUSH);
569 dc.SetPen(*wxTRANSPARENT_PEN);
570 dc.DrawRectangle(rectUpdate);
572 // under Unix we use "--sync" X option for this
578 #endif // WXDEBUG_SCROLLBAR
581 wxOrientation orient = scrollbar->IsVertical() ? wxVERTICAL
585 for ( int nBar = 0; nBar < 2; nBar++ )
587 wxScrollBar::Element elem =
588 (wxScrollBar::Element)(wxScrollBar::Element_Bar_1 + nBar);
590 wxRect rectBar = m_renderer->GetScrollbarRect(scrollbar, elem);
592 if ( rgnUpdate.Contains(rectBar) )
594 wxLogTrace(_T("scrollbar"),
595 _T("drawing bar part %d at (%d, %d)-(%d, %d)"),
600 rectBar.GetBottom());
602 m_renderer->DrawScrollbarShaft(m_dc,
605 scrollbar->GetState(elem));
610 for ( int nArrow = 0; nArrow < 2; nArrow++ )
612 wxScrollBar::Element elem =
613 (wxScrollBar::Element)(wxScrollBar::Element_Arrow_Line_1 + nArrow);
615 wxRect rectArrow = m_renderer->GetScrollbarRect(scrollbar, elem);
616 if ( rgnUpdate.Contains(rectArrow) )
618 wxLogTrace(_T("scrollbar"),
619 _T("drawing arrow %d at (%d, %d)-(%d, %d)"),
623 rectArrow.GetRight(),
624 rectArrow.GetBottom());
626 scrollbar->GetArrows().DrawArrow
628 (wxScrollArrows::Arrow)nArrow,
631 TRUE // draw a scrollbar arrow, not just an arrow
636 // TODO: support for page arrows
639 wxScrollBar::Element elem = wxScrollBar::Element_Thumb;
640 wxRect rectThumb = m_renderer->GetScrollbarRect(scrollbar, elem);
641 if ( rectThumb.width && rectThumb.height && rgnUpdate.Contains(rectThumb) )
643 wxLogTrace(_T("scrollbar"),
644 _T("drawing thumb at (%d, %d)-(%d, %d)"),
647 rectThumb.GetRight(),
648 rectThumb.GetBottom());
650 m_renderer->DrawScrollbarThumb(m_dc,
653 scrollbar->GetState(elem));
657 void wxControlRenderer::DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
659 wxASSERT_MSG( x1 == x2 || y1 == y2,
660 _T("line must be either horizontal or vertical") );
663 m_renderer->DrawVerticalLine(m_dc, x1, y1, y2);
665 m_renderer->DrawHorizontalLine(m_dc, y1, x1, x2);
670 void wxControlRenderer::DrawItems(const wxListBox *lbox,
671 size_t itemFirst, size_t itemLast)
673 DoDrawItems(lbox, itemFirst, itemLast);
676 void wxControlRenderer::DoDrawItems(const wxListBox *lbox,
677 size_t itemFirst, size_t itemLast,
680 // prepare for the drawing: calc the initial position
681 wxCoord lineHeight = lbox->GetLineHeight();
683 // note that SetClippingRegion() needs the physical (unscrolled)
684 // coordinates while we use the logical (scrolled) ones for the drawing
687 wxSize size = lbox->GetClientSize();
689 rect.height = size.y;
691 // keep the text inside the client rect or we will overwrite the vertical
692 // scrollbar for the long strings
693 m_dc.SetClippingRegion(rect.x, rect.y, rect.width + 1, rect.height + 1);
695 // adjust the rect position now
696 lbox->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
697 rect.y += itemFirst*lineHeight;
698 rect.height = lineHeight;
700 // the rect should go to the right visible border so adjust the width if x
701 // is shifted (rightmost point should stay the same)
702 rect.width -= rect.x;
704 // we'll keep the text colour unchanged
705 m_dc.SetTextForeground(lbox->GetForegroundColour());
707 // an item should have the focused rect only when the lbox has focus, so
708 // make sure that we never set wxCONTROL_FOCUSED flag if it doesn't
709 int itemCurrent = wxWindow::FindFocus() == (wxWindow *)lbox // cast needed
710 ? lbox->GetCurrentItem()
712 for ( size_t n = itemFirst; n < itemLast; n++ )
715 if ( (int)n == itemCurrent )
716 flags |= wxCONTROL_FOCUSED;
717 if ( lbox->IsSelected(n) )
718 flags |= wxCONTROL_SELECTED;
720 #if wxUSE_CHECKLISTBOX
723 wxCheckListBox *checklstbox = wxStaticCast(lbox, wxCheckListBox);
724 if ( checklstbox->IsChecked(n) )
725 flags |= wxCONTROL_CHECKED;
727 m_renderer->DrawCheckItem(m_dc, lbox->GetString(n),
733 #endif // wxUSE_CHECKLISTBOX
735 m_renderer->DrawItem(m_dc, lbox->GetString(n), rect, flags);
738 rect.y += lineHeight;
742 #endif // wxUSE_LISTBOX
744 #if wxUSE_CHECKLISTBOX
746 void wxControlRenderer::DrawCheckItems(const wxCheckListBox *lbox,
747 size_t itemFirst, size_t itemLast)
749 DoDrawItems(lbox, itemFirst, itemLast, TRUE);
752 #endif // wxUSE_CHECKLISTBOX
756 void wxControlRenderer::DrawProgressBar(const wxGauge *gauge)
759 m_dc.SetBrush(wxBrush(m_window->GetBackgroundColour(), wxSOLID));
760 m_dc.SetPen(*wxTRANSPARENT_PEN);
761 m_dc.DrawRectangle(m_rect);
763 int max = gauge->GetRange();
770 // calc the filled rect
771 int pos = gauge->GetValue();
772 int left = max - pos;
774 wxRect rect = m_rect;
775 rect.Deflate(1); // FIXME this depends on the border width
777 wxColour col = m_window->UseFgCol() ? m_window->GetForegroundColour()
778 : wxTHEME_COLOUR(GAUGE);
779 m_dc.SetBrush(wxBrush(col, wxSOLID));
781 if ( gauge->IsSmooth() )
783 // just draw the rectangle in one go
784 if ( gauge->IsVertical() )
786 // vert bars grow from bottom to top
787 wxCoord dy = ((rect.height - 1) * left) / max;
793 // grow from left to right
794 rect.width -= ((rect.width - 1) * left) / max;
797 m_dc.DrawRectangle(rect);
801 wxSize sizeStep = m_renderer->GetProgressBarStep();
802 int step = gauge->IsVertical() ? sizeStep.y : sizeStep.x;
804 // we divide by it below!
805 wxCHECK_RET( step, _T("invalid wxGauge step") );
807 // round up to make the progress appear to start faster
808 int lenTotal = gauge->IsVertical() ? rect.height : rect.width;
809 int steps = ((lenTotal + step - 1) * pos) / (max * step);
811 // calc the coords of one small rect
813 if ( gauge->IsVertical() )
815 // draw from bottom to top: so first set y to the bottom
816 rect.y += rect.height - 1;
818 // then adjust the height
821 // and then adjust y again to be what it should for the first rect
822 rect.y -= rect.height;
827 // remember that this will be the coord which will change
835 // don't leave 2 empty pixels in the beginning
845 for ( int n = 0; n < steps; n++ )
847 wxRect rectSegment = rect;
848 rectSegment.Deflate(dx, dy);
850 m_dc.DrawRectangle(rectSegment);
855 // this can only happen for the last step of vertical gauge
856 rect.height = *px - step - 1;
859 else if ( *px > lenTotal - step )
861 // this can only happen for the last step of horizontal gauge
862 rect.width = lenTotal - *px - 1;
868 #endif // wxUSE_GAUGE