size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect, const gfx::Rect& aTexCoordRect, decomposedRectArrayT* aLayerRects, decomposedRectArrayT* aTextureRects) { gfx::Rect texCoordRect = aTexCoordRect; // If the texture should be flipped, it will have negative height. Detect that // here and compensate for it. We will flip each rect as we emit it. bool flipped = false; if (texCoordRect.height < 0) { flipped = true; texCoordRect.y += texCoordRect.height; texCoordRect.height = -texCoordRect.height; } // Wrap the texture coordinates so they are within [0,1] and cap width/height // at 1. We rely on this below. texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x), WrapTexCoord(texCoordRect.y)), gfx::Size(std::min(texCoordRect.width, 1.0f), std::min(texCoordRect.height, 1.0f))); NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f && texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f && texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f && texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f && texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f && texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f, "We just wrapped the texture coordinates, didn't we?"); // Get the top left and bottom right points of the rectangle. Note that // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2]. gfx::Point tl = texCoordRect.TopLeft(); gfx::Point br = texCoordRect.BottomRight(); NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f && tl.y >= 0.0f && tl.y <= 1.0f && br.x >= tl.x && br.x <= 2.0f && br.y >= tl.y && br.y <= 2.0f && FuzzyLTE(br.x - tl.x, 1.0f) && FuzzyLTE(br.y - tl.y, 1.0f), "Somehow generated invalid texture coordinates"); // Then check if we wrap in either the x or y axis. bool xwrap = br.x > 1.0f; bool ywrap = br.y > 1.0f; // If xwrap is false, the texture will be sampled from tl.x .. br.x. // If xwrap is true, then it will be split into tl.x .. 1.0, and // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination // rectangle is also split appropriately, according to the calculated // xmid/ymid values. if (!xwrap && !ywrap) { SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, aRect.XMost(), aRect.YMost(), tl.x, tl.y, br.x, br.y, flipped); return 1; } // If we are dealing with wrapping br.x and br.y are greater than 1.0 so // wrap them here as well. br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x, ywrap ? WrapTexCoord(br.y) : br.y); // If we wrap around along the x axis, we will draw first from // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above). // The same applies for the Y axis. The midpoints we calculate here are // only valid if we actually wrap around. GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width; GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height; NS_ASSERTION(!xwrap || (xmid > aRect.x && xmid < aRect.XMost() && FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)), "xmid should be within [x,XMost()] and the wrapped rect should have the same width"); NS_ASSERTION(!ywrap || (ymid > aRect.y && ymid < aRect.YMost() && FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)), "ymid should be within [y,YMost()] and the wrapped rect should have the same height"); if (!xwrap && ywrap) { SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, aRect.XMost(), ymid, tl.x, tl.y, br.x, 1.0f, flipped); SetRects(1, aLayerRects, aTextureRects, aRect.x, ymid, aRect.XMost(), aRect.YMost(), tl.x, 0.0f, br.x, br.y, flipped); return 2; } if (xwrap && !ywrap) { SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, xmid, aRect.YMost(), tl.x, tl.y, 1.0f, br.y, flipped); SetRects(1, aLayerRects, aTextureRects, xmid, aRect.y, aRect.XMost(), aRect.YMost(), 0.0f, tl.y, br.x, br.y, flipped); return 2; } SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, xmid, ymid, tl.x, tl.y, 1.0f, 1.0f, flipped); SetRects(1, aLayerRects, aTextureRects, xmid, aRect.y, aRect.XMost(), ymid, 0.0f, tl.y, br.x, 1.0f, flipped); SetRects(2, aLayerRects, aTextureRects, aRect.x, ymid, xmid, aRect.YMost(), tl.x, 0.0f, 1.0f, br.y, flipped); SetRects(3, aLayerRects, aTextureRects, xmid, ymid, aRect.XMost(), aRect.YMost(), 0.0f, 0.0f, br.x, br.y, flipped); return 4; }
void clRowEntry::Render(wxWindow* win, wxDC& dc, const clColours& c, int row_index, clSearchText* searcher) { wxUnusedVar(searcher); wxRect rowRect = GetItemRect(); bool zebraColouring = (m_tree->HasStyle(wxTR_ROW_LINES) || m_tree->HasStyle(wxDV_ROW_LINES)); bool even_row = ((row_index % 2) == 0); // Define the clipping region bool hasHeader = (m_tree->GetHeader() && !m_tree->GetHeader()->empty()); // Not cell related clColours colours = c; if(zebraColouring) { // Set Zebra colouring, only if no user colour was provided for the given line colours.SetItemBgColour(even_row ? c.GetAlternateColour() : c.GetBgColour()); } // Override default item bg colour with the user's one if(GetBgColour().IsOk()) { colours.SetItemBgColour(GetBgColour()); } wxRect selectionRect = rowRect; wxPoint deviceOrigin = dc.GetDeviceOrigin(); selectionRect.SetX(-deviceOrigin.x); if(IsSelected()) { DrawSimpleSelection(win, dc, selectionRect, colours); } else if(IsHovered()) { dc.SetPen(colours.GetHoverBgColour()); dc.SetBrush(colours.GetHoverBgColour()); dc.DrawRectangle(selectionRect); } else if(colours.GetItemBgColour().IsOk()) { dc.SetBrush(colours.GetItemBgColour()); dc.SetPen(colours.GetItemBgColour()); dc.DrawRectangle(selectionRect); } // Per cell drawings for(size_t i = 0; i < m_cells.size(); ++i) { bool last_cell = (i == (m_cells.size() - 1)); colours = c; // reset the colours wxFont f = clScrolledPanel::GetDefaultFont(); clCellValue& cell = GetColumn(i); if(cell.GetFont().IsOk()) { f = cell.GetFont(); } if(cell.GetTextColour().IsOk()) { colours.SetItemTextColour(cell.GetTextColour()); } if(cell.GetBgColour().IsOk()) { colours.SetItemBgColour(cell.GetBgColour()); } dc.SetFont(f); wxColour buttonColour = IsSelected() ? colours.GetSelbuttonColour() : colours.GetButtonColour(); wxRect cellRect = GetCellRect(i); // We use a helper class to clip the drawings this ensures that if we exit the scope // the clipping region is restored properly clClipperHelper clipper(dc); if(hasHeader) { clipper.Clip(cellRect); } int textXOffset = cellRect.GetX(); if((i == 0) && !IsListItem()) { // The expand button is only make sense for the first cell if(HasChildren()) { wxRect buttonRect = GetButtonRect(); buttonRect.Deflate(1); textXOffset += buttonRect.GetWidth(); if(m_tree->IsNativeTheme() && !IS_OSX) { int flags = wxCONTROL_CURRENT; if(IsExpanded()) { flags |= wxCONTROL_EXPANDED; } int button_width = wxSystemSettings::GetMetric(wxSYS_SMALLICON_X); wxRect modButtonRect = buttonRect; modButtonRect.SetWidth(button_width); modButtonRect.SetHeight(button_width); modButtonRect = modButtonRect.CenterIn(buttonRect); wxRendererNative::Get().DrawTreeItemButton(win, dc, modButtonRect, flags); } else { wxRect buttonRect = GetButtonRect(); if(textXOffset >= cellRect.GetWidth()) { // if we cant draw the button (off screen etc) SetRects(GetItemRect(), wxRect()); continue; } buttonRect.Deflate((buttonRect.GetWidth() / 3), (buttonRect.GetHeight() / 3)); wxRect tribtn = buttonRect; dc.SetPen(wxPen(buttonColour, 2)); if(IsExpanded()) { tribtn.SetHeight(tribtn.GetHeight() - tribtn.GetHeight() / 2); tribtn = tribtn.CenterIn(buttonRect); wxPoint middleLeft = wxPoint((tribtn.GetLeft() + tribtn.GetWidth() / 2), tribtn.GetBottom()); dc.DrawLine(tribtn.GetTopLeft(), middleLeft); dc.DrawLine(tribtn.GetTopRight(), middleLeft); } else { tribtn.SetWidth(tribtn.GetWidth() - tribtn.GetWidth() / 2); tribtn = tribtn.CenterIn(buttonRect); wxPoint middleLeft = wxPoint(tribtn.GetRight(), (tribtn.GetY() + (tribtn.GetHeight() / 2))); wxPoint p1 = tribtn.GetTopLeft(); wxPoint p2 = tribtn.GetBottomLeft(); dc.DrawLine(p1, middleLeft); dc.DrawLine(middleLeft, p2); } } } else { wxRect buttonRect(rowRect); buttonRect.SetWidth(rowRect.GetHeight()); buttonRect.Deflate(1); textXOffset += buttonRect.GetWidth(); if(textXOffset >= cellRect.GetWidth()) { SetRects(GetItemRect(), wxRect()); continue; } } } int itemIndent = IsListItem() ? clHeaderItem::X_SPACER : (GetIndentsCount() * m_tree->GetIndent()); int bitmapIndex = cell.GetBitmapIndex(); if(IsExpanded() && HasChildren() && cell.GetBitmapSelectedIndex() != wxNOT_FOUND) { bitmapIndex = cell.GetBitmapSelectedIndex(); } // Draw checkbox if(cell.IsBool()) { // Render the checkbox textXOffset += X_SPACER; int checkboxSize = GetCheckBoxWidth(win); wxRect checkboxRect = wxRect(textXOffset, rowRect.GetY(), checkboxSize, checkboxSize); checkboxRect = checkboxRect.CenterIn(rowRect, wxVERTICAL); dc.SetPen(colours.GetItemTextColour()); RenderCheckBox(win, dc, colours, checkboxRect, cell.GetValueBool()); cell.SetCheckboxRect(checkboxRect); textXOffset += checkboxRect.GetWidth(); textXOffset += X_SPACER; } else { cell.SetCheckboxRect(wxRect()); // clear the checkbox rect } // Draw the bitmap if(bitmapIndex != wxNOT_FOUND) { const wxBitmap& bmp = m_tree->GetBitmap(bitmapIndex); if(bmp.IsOk()) { textXOffset += IsListItem() ? 0 : X_SPACER; int bitmapY = rowRect.GetY() + ((rowRect.GetHeight() - bmp.GetScaledHeight()) / 2); // if((textXOffset + bmp.GetScaledWidth()) >= cellRect.GetWidth()) { continue; } dc.DrawBitmap(bmp, itemIndent + textXOffset, bitmapY, true); textXOffset += bmp.GetScaledWidth(); textXOffset += X_SPACER; } } // Draw the text wxRect textRect(dc.GetTextExtent(cell.GetValueString())); textRect = textRect.CenterIn(rowRect, wxVERTICAL); int textY = textRect.GetY(); int textX = (i == 0 ? itemIndent : clHeaderItem::X_SPACER) + textXOffset; RenderText(win, dc, colours, cell.GetValueString(), textX, textY, i); textXOffset += textRect.GetWidth(); textXOffset += X_SPACER; if(cell.IsChoice()) { // draw the drop down arrow. Make it aligned to the right wxRect dropDownRect(cellRect.GetTopRight().x - rowRect.GetHeight(), rowRect.GetY(), rowRect.GetHeight(), rowRect.GetHeight()); dropDownRect = dropDownRect.CenterIn(rowRect, wxVERTICAL); DrawingUtils::DrawDropDownArrow(win, dc, dropDownRect, wxNullColour); // Keep the rect to test clicks cell.SetDropDownRect(dropDownRect); textXOffset += dropDownRect.GetWidth(); textXOffset += X_SPACER; // Draw a separator line between the drop down arrow and the rest of the cell content dropDownRect.Deflate(3); dropDownRect = dropDownRect.CenterIn(rowRect, wxVERTICAL); dc.SetPen(wxPen(colours.GetHeaderVBorderColour(), 1, PEN_STYLE)); dc.DrawLine(dropDownRect.GetTopLeft(), dropDownRect.GetBottomLeft()); } else { cell.SetDropDownRect(wxRect()); } if(!last_cell) { cellRect.SetHeight(rowRect.GetHeight()); dc.SetPen(wxPen(colours.GetHeaderVBorderColour(), 1, PEN_STYLE)); dc.DrawLine(cellRect.GetTopRight(), cellRect.GetBottomRight()); } } }