summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
Diffstat (limited to 'wx+')
-rw-r--r--wx+/font_size.h2
-rw-r--r--wx+/graph.cpp25
-rw-r--r--wx+/graph.h18
-rw-r--r--wx+/grid.cpp482
-rw-r--r--wx+/grid.h105
-rw-r--r--wx+/image_resources.cpp7
-rw-r--r--wx+/image_resources.h1
-rw-r--r--wx+/image_tools.h16
-rw-r--r--wx+/tooltip.h5
9 files changed, 365 insertions, 296 deletions
diff --git a/wx+/font_size.h b/wx+/font_size.h
index 2302e056..2858afb6 100644
--- a/wx+/font_size.h
+++ b/wx+/font_size.h
@@ -69,7 +69,7 @@ void setMainInstructionFont(wxWindow& control)
0, // _In_ int iStateId,
TMT_TEXTCOLOR, // _In_ int iPropId,
&cr) == S_OK) // _Out_ COLORREF *pColor
- control.SetForegroundColour(wxColour(cr));
+ control.SetForegroundColour(wxColor(cr));
}
#elif defined ZEN_LINUX //https://developer.gnome.org/hig-book/3.2/hig-book.html#alert-text
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index bf1f1567..f39d29f5 100644
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -19,9 +19,6 @@ using namespace zen;
const wxEventType zen::wxEVT_GRAPH_SELECTION = wxNewEventType();
-//for some buggy reason MSVC isn't able to use a temporary as a default argument
-const std::shared_ptr<LabelFormatter> Graph2D::MainAttributes::defaultFormat = std::make_shared<DecimalNumberFormatter>();
-
double zen::nextNiceNumber(double blockSize) //round to next number which is a convenient to read block size
{
@@ -48,25 +45,25 @@ wxColor getDefaultColor(size_t pos)
switch (pos % 10)
{
case 0:
- return wxColor(0, 69, 134); //blue
+ return { 0, 69, 134 }; //blue
case 1:
- return wxColor(255, 66, 14); //red
+ return { 255, 66, 14 }; //red
case 2:
- return wxColor(255, 211, 32); //yellow
+ return { 255, 211, 32 }; //yellow
case 3:
- return wxColor(87, 157, 28); //green
+ return { 87, 157, 28 }; //green
case 4:
- return wxColor(126, 0, 33); //royal
+ return { 126, 0, 33 }; //royal
case 5:
- return wxColor(131, 202, 255); //light blue
+ return { 131, 202, 255 }; //light blue
case 6:
- return wxColor(49, 64, 4); //dark green
+ return { 49, 64, 4 }; //dark green
case 7:
- return wxColor(174, 207, 0); //light green
+ return { 174, 207, 0 }; //light green
case 8:
- return wxColor(75, 31, 111); //purple
+ return { 75, 31, 111 }; //purple
case 9:
- return wxColor(255, 149, 14); //orange
+ return { 255, 149, 14 }; //orange
}
assert(false);
return *wxBLACK;
@@ -574,7 +571,7 @@ void Graph2D::render(wxDC& dc) const
{
//paint graph background (excluding label area)
- wxDCPenChanger dummy (dc, wxColour(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal...
+ wxDCPenChanger dummy (dc, wxColor(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal...
wxDCBrushChanger dummy2(dc, attr.backgroundColor);
//accessibility: consider system text and background colors; small drawback: color of graphs is NOT connected to the background! => responsibility of client to use correct colors
diff --git a/wx+/graph.h b/wx+/graph.h
index 7b61858d..b9873bd8 100644
--- a/wx+/graph.h
+++ b/wx+/graph.h
@@ -179,18 +179,18 @@ public:
{
public:
CurveAttributes() {} //required by GCC
- CurveAttributes& setColor (const wxColour& col) { color = col; autoColor = false; return *this; }
- CurveAttributes& fillCurveArea(const wxColour& col) { fillColor = col; drawCurveArea = true; return *this; }
+ CurveAttributes& setColor (const wxColor& col) { color = col; autoColor = false; return *this; }
+ CurveAttributes& fillCurveArea(const wxColor& col) { fillColor = col; drawCurveArea = true; return *this; }
CurveAttributes& setLineWidth(size_t width) { lineWidth = static_cast<int>(width); return *this; }
private:
friend class Graph2D;
bool autoColor = true;
- wxColour color;
+ wxColor color;
bool drawCurveArea = false;
- wxColour fillColor;
+ wxColor fillColor;
int lineWidth = 2;
};
@@ -239,16 +239,14 @@ public:
MainAttributes& setAutoSize() { minXauto = maxXauto = minYauto = maxYauto = true; return *this; }
- static const std::shared_ptr<LabelFormatter> defaultFormat;
-
- MainAttributes& setLabelX(PosLabelX posX, size_t height = 25, const std::shared_ptr<LabelFormatter>& newLabelFmt = defaultFormat)
+ MainAttributes& setLabelX(PosLabelX posX, size_t height = 25, std::shared_ptr<LabelFormatter> newLabelFmt = std::make_shared<DecimalNumberFormatter>())
{
labelposX = posX;
xLabelHeight = static_cast<int>(height);
labelFmtX = newLabelFmt;
return *this;
}
- MainAttributes& setLabelY(PosLabelY posY, size_t width = 60, const std::shared_ptr<LabelFormatter>& newLabelFmt = defaultFormat)
+ MainAttributes& setLabelY(PosLabelY posY, size_t width = 60, std::shared_ptr<LabelFormatter> newLabelFmt = std::make_shared<DecimalNumberFormatter>())
{
labelposY = posY;
yLabelWidth = static_cast<int>(width);
@@ -258,7 +256,7 @@ public:
MainAttributes& setCornerText(const wxString& txt, PosCorner pos) { cornerTexts[pos] = txt; return *this; }
- MainAttributes& setBackgroundColor(const wxColour& col) { backgroundColor = col; return *this; }
+ MainAttributes& setBackgroundColor(const wxColor& col) { backgroundColor = col; return *this; }
MainAttributes& setSelectionMode(SelMode mode) { mouseSelMode = mode; return *this; }
@@ -285,7 +283,7 @@ public:
std::map<PosCorner, wxString> cornerTexts;
- wxColour backgroundColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+ wxColor backgroundColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
SelMode mouseSelMode = SELECT_RECTANGLE;
};
void setAttributes(const MainAttributes& newAttr) { attr = newAttr; Refresh(); }
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 26186a09..56556797 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -26,8 +26,11 @@
using namespace zen;
-wxColor zen::getColorSelectionGradientFrom() { return wxColor(137, 172, 255); } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1
-wxColor zen::getColorSelectionGradientTo () { return wxColor(225, 234, 255); } // HSL: 158, 255, 240 HSV: 222, 0.12, 1
+wxColor zen::getColorSelectionGradientFrom() { return { 137, 172, 255 }; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1
+wxColor zen::getColorSelectionGradientTo () { return { 225, 234, 255 }; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1
+
+const int GridData::COLUMN_GAP_LEFT = 4;
+
void zen::clearArea(wxDC& dc, const wxRect& rect, const wxColor& col)
{
@@ -36,43 +39,41 @@ void zen::clearArea(wxDC& dc, const wxRect& rect, const wxColor& col)
dc.DrawRectangle(rect);
}
-const int GridData::COLUMN_GAP_LEFT = 4;
namespace
{
+//let's NOT create wxWidgets objects statically:
//------------------------------ Grid Parameters --------------------------------
-wxColor getColorLabelText() { return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); }
+inline wxColor getColorLabelText() { return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); }
+inline wxColor getColorGridLine() { return { 192, 192, 192 }; } //light grey
-wxColor getColorLabelGradientFrom () { return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); }
-wxColor getColorLabelGradientTo () { return wxColour(200, 200, 200); } //light grey
+inline wxColor getColorLabelGradientFrom() { return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); }
+inline wxColor getColorLabelGradientTo () { return { 200, 200, 200 }; } //light grey
-wxColor getColorLabelGradientFocusFrom() { return getColorLabelGradientFrom(); }
-wxColor getColorLabelGradientFocusTo () { return getColorSelectionGradientFrom(); }
+inline wxColor getColorLabelGradientFocusFrom() { return getColorLabelGradientFrom(); }
+inline wxColor getColorLabelGradientFocusTo () { return getColorSelectionGradientFrom(); }
-const double MOUSE_DRAG_ACCELERATION = 1.5; //unit: [rows / (pixel * sec)] -> same value as Explorer!
+const double MOUSE_DRAG_ACCELERATION = 1.5; //unit: [rows / (pixel * sec)] -> same value like Explorer!
const int DEFAULT_COL_LABEL_BORDER = 6; //top + bottom border in addition to label height
-//const int COLUMN_LABEL_BORDER = GridData::COLUMN_GAP_LEFT;
const int COLUMN_MOVE_DELAY = 5; //unit: [pixel] (from Explorer)
const int COLUMN_MIN_WIDTH = 40; //only honored when resizing manually!
const int ROW_LABEL_BORDER = 3;
const int COLUMN_RESIZE_TOLERANCE = 6; //unit [pixel]
const int COLUMN_FILL_GAP_TOLERANCE = 10; //enlarge column to fill full width when resizing
-const wxColor colorGridLine = wxColour(192, 192, 192); //light grey
-
const bool fillGapAfterColumns = true; //draw rows/column label to fill full window width; may become an instance variable some time?
}
//----------------------------------------------------------------------------------------------------------------
const wxEventType zen::EVENT_GRID_MOUSE_LEFT_DOUBLE = wxNewEventType();
-const wxEventType zen::EVENT_GRID_COL_LABEL_MOUSE_LEFT = wxNewEventType();
-const wxEventType zen::EVENT_GRID_COL_LABEL_MOUSE_RIGHT = wxNewEventType();
-const wxEventType zen::EVENT_GRID_COL_RESIZE = wxNewEventType();
const wxEventType zen::EVENT_GRID_MOUSE_LEFT_DOWN = wxNewEventType();
const wxEventType zen::EVENT_GRID_MOUSE_LEFT_UP = wxNewEventType();
const wxEventType zen::EVENT_GRID_MOUSE_RIGHT_DOWN = wxNewEventType();
const wxEventType zen::EVENT_GRID_MOUSE_RIGHT_UP = wxNewEventType();
const wxEventType zen::EVENT_GRID_SELECT_RANGE = wxNewEventType();
+const wxEventType zen::EVENT_GRID_COL_LABEL_MOUSE_LEFT = wxNewEventType();
+const wxEventType zen::EVENT_GRID_COL_LABEL_MOUSE_RIGHT = wxNewEventType();
+const wxEventType zen::EVENT_GRID_COL_RESIZE = wxNewEventType();
//----------------------------------------------------------------------------------------------------------------
void GridData::renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected)
@@ -81,7 +82,7 @@ void GridData::renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool
}
-void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected)
+void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected, HoverArea rowHover)
{
wxRect rectTmp = drawCellBorder(dc, rect);
@@ -99,7 +100,7 @@ int GridData::getBestSize(wxDC& dc, size_t row, ColumnType colType)
wxRect GridData::drawCellBorder(wxDC& dc, const wxRect& rect) //returns remaining rectangle
{
- wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID));
+ wxDCPenChanger dummy2(dc, wxPen(getColorGridLine(), 1, wxSOLID));
dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight());
dc.DrawLine(rect.GetBottomRight(), rect.GetTopRight() + wxPoint(0, -1));
@@ -298,7 +299,7 @@ protected:
//wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width)
if (!tt)
SetToolTip(new wxToolTip(L"a b\n\
- a b")); //ugly, but is working (on Windows)
+ a b")); //ugly, but working (on Windows)
tt = GetToolTip(); //should be bound by now
assert(tt);
if (tt)
@@ -447,14 +448,16 @@ public:
return -1;
}
- int getRowHeight() const { return std::max(1, rowHeight); } //guarantees to return size >= 1 !
- void setRowHeight(int height) { rowHeight = height; }
+ int getRowHeight() const { return rowHeight; } //guarantees to return size >= 1 !
+ void setRowHeight(int height) { assert(height > 0); rowHeight = std::max(1, height); }
- wxRect getRowLabelArea(ptrdiff_t row) const
+ wxRect getRowLabelArea(size_t row) const //returns empty rect if row not found
{
assert(GetClientAreaOrigin() == wxPoint());
- return wxRect(wxPoint(0, rowHeight * row),
- wxSize(GetClientSize().GetWidth(), rowHeight));
+ if (row < refParent().getRowCount())
+ return wxRect(wxPoint(0, rowHeight * row),
+ wxSize(GetClientSize().GetWidth(), rowHeight));
+ return wxRect();
}
std::pair<ptrdiff_t, ptrdiff_t> getRowsOnClient(const wxRect& clientRect) const //returns range [begin, end)
@@ -503,8 +506,8 @@ private:
auto rowRange = getRowsOnClient(rect); //returns range [begin, end)
for (auto row = rowRange.first; row < rowRange.second; ++row)
{
- wxRect singleLabelArea = getRowLabelArea(row);
- if (singleLabelArea.GetHeight() > 0)
+ wxRect singleLabelArea = getRowLabelArea(row); //returns empty rect if row not found
+ if (singleLabelArea.height > 0)
{
singleLabelArea.y = refParent().CalcScrolledPosition(singleLabelArea.GetTopLeft()).y;
drawRowLabel(dc, singleLabelArea, row);
@@ -653,7 +656,7 @@ private:
const int clientWidth = GetClientSize().GetWidth(); //need reliable, stable width in contrast to rect.width
if (totalWidth < clientWidth)
- drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(clientWidth - totalWidth, colLabelHeight)), absWidths.size(), DUMMY_COLUMN_TYPE);
+ drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(clientWidth - totalWidth, colLabelHeight)), absWidths.size(), ColumnType::NONE);
}
}
@@ -661,9 +664,9 @@ private:
{
if (auto dataView = refParent().getDataProvider())
{
- const bool isHighlighted = activeResizing ? col == activeResizing->getColumn () : //highlight column on mouse-over
- activeMove ? col == activeMove ->getColumnFrom() :
- highlightCol ? col == *highlightCol :
+ const bool isHighlighted = activeResizing ? col == activeResizing ->getColumn () : //highlight column on mouse-over
+ activeClickOrMove ? col == activeClickOrMove->getColumnFrom() :
+ highlightCol ? col == *highlightCol :
false;
RecursiveDcClipper clip(dc, rect);
@@ -671,11 +674,11 @@ private:
//draw move target location
if (refParent().allowColumnMove)
- if (activeMove && activeMove->isRealMove())
+ if (activeClickOrMove && activeClickOrMove->isRealMove())
{
- if (col + 1 == activeMove->refColumnTo()) //handle pos 1, 2, .. up to "at end" position
+ if (col + 1 == activeClickOrMove->refColumnTo()) //handle pos 1, 2, .. up to "at end" position
dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
- else if (col == activeMove->refColumnTo() && col == 0) //pos 0
+ else if (col == activeClickOrMove->refColumnTo() && col == 0) //pos 0
dc.GradientFillLinear(wxRect(rect.GetTopLeft(), rect.GetBottomLeft() + wxPoint(2, 0)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
}
}
@@ -687,7 +690,7 @@ private:
refParent().getMainWin().SetFocus();
activeResizing.reset();
- activeMove.reset();
+ activeClickOrMove.reset();
if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
@@ -698,7 +701,7 @@ private:
activeResizing = std::make_unique<ColumnResizing>(*this, action->col, *colWidth, event.GetPosition().x);
}
else //a move or single click
- activeMove = std::make_unique<ColumnMove>(*this, action->col, event.GetPosition().x);
+ activeClickOrMove = std::make_unique<ColumnMove>(*this, action->col, event.GetPosition().x);
}
event.Skip();
}
@@ -707,14 +710,14 @@ private:
{
activeResizing.reset(); //nothing else to do, actual work done by onMouseMovement()
- if (activeMove)
+ if (activeClickOrMove)
{
- if (activeMove->isRealMove())
+ if (activeClickOrMove->isRealMove())
{
if (refParent().allowColumnMove)
{
- const auto colFrom = activeMove->getColumnFrom();
- auto colTo = activeMove->refColumnTo();
+ const size_t colFrom = activeClickOrMove->getColumnFrom();
+ size_t colTo = activeClickOrMove->refColumnTo();
if (colTo > colFrom) //simulate "colFrom" deletion
--colTo;
@@ -724,10 +727,10 @@ private:
}
else //notify single label click
{
- if (const Opt<ColumnType> colType = refParent().colToType(activeMove->getColumnFrom()))
- sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, -1, *colType));
+ if (const Opt<ColumnType> colType = refParent().colToType(activeClickOrMove->getColumnFrom()))
+ sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, *colType));
}
- activeMove.reset();
+ activeClickOrMove.reset();
}
refParent().updateWindowSizes(); //looks strange if done during onMouseMovement()
@@ -738,7 +741,7 @@ private:
void onMouseCaptureLost(wxMouseCaptureLostEvent& event) override
{
activeResizing.reset();
- activeMove.reset();
+ activeClickOrMove.reset();
Refresh();
//event.Skip(); -> we DID handle it!
}
@@ -770,22 +773,22 @@ private:
refParent().setColumnWidth(newWidth, col, ALLOW_GRID_EVENT);
//check if there's a small gap after last column, if yes, fill it
- int gapWidth = GetClientSize().GetWidth() - refParent().getColWidthsSum(GetClientSize().GetWidth());
+ const int gapWidth = GetClientSize().GetWidth() - refParent().getColWidthsSum(GetClientSize().GetWidth());
if (std::abs(gapWidth) < COLUMN_FILL_GAP_TOLERANCE)
refParent().setColumnWidth(newWidth + gapWidth, col, ALLOW_GRID_EVENT);
refParent().Refresh(); //refresh columns on main grid as well!
}
- else if (activeMove)
+ else if (activeClickOrMove)
{
const int clientPosX = event.GetPosition().x;
- if (std::abs(clientPosX - activeMove->getStartPosX()) > COLUMN_MOVE_DELAY) //real move (not a single click)
+ if (std::abs(clientPosX - activeClickOrMove->getStartPosX()) > COLUMN_MOVE_DELAY) //real move (not a single click)
{
- activeMove->setRealMove();
+ activeClickOrMove->setRealMove();
const ptrdiff_t col = refParent().clientPosToMoveTargetColumn(event.GetPosition());
if (col >= 0)
- activeMove->refColumnTo() = col;
+ activeClickOrMove->refColumnTo() = col;
}
}
else
@@ -806,13 +809,13 @@ private:
}
}
- //update tooltip
const std::wstring toolTip = [&]
{
const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
- if (const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x))
+ const ColumnType colType = refParent().getColumnAtPos(absPos.x).colType; //returns ColumnType::NONE if no column at x position!
+ if (colType != ColumnType::NONE)
if (auto prov = refParent().getDataProvider())
- return prov->getToolTip(*ct);
+ return prov->getToolTip(colType);
return std::wstring();
}();
setToolTip(toolTip);
@@ -833,19 +836,19 @@ private:
if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
if (const Opt<ColumnType> colType = refParent().colToType(action->col))
- sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, -1, *colType)); //notify right click
+ sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, *colType)); //notify right click
else assert(false);
}
else
//notify right click (on free space after last column)
if (fillGapAfterColumns)
- sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, -1, DUMMY_COLUMN_TYPE));
+ sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, ColumnType::NONE));
event.Skip();
}
std::unique_ptr<ColumnResizing> activeResizing;
- std::unique_ptr<ColumnMove> activeMove;
+ std::unique_ptr<ColumnMove> activeClickOrMove;
Opt<size_t> highlightCol; //column during mouse-over
};
@@ -892,17 +895,6 @@ private:
wxDCTextColourChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels
- const int rowHeight = rowLabelWin_.getRowHeight();
-
- //why again aren't we using RowLabelWin::getRowsOnClient() here?
- const wxPoint topLeft = refParent().CalcUnscrolledPosition(rect.GetTopLeft());
- const wxPoint bottomRight = refParent().CalcUnscrolledPosition(rect.GetBottomRight());
-
- const int rowFirst = std::max(topLeft .y / rowHeight, 0); // [rowFirst, rowLast)
- const int rowLast = std::min(bottomRight.y / rowHeight + 1, static_cast<int>(refParent().getRowCount()));
-
- wxPoint cellAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0))); //client coordinates
-
std::vector<ColumnWidth> absWidths = refParent().getColWidths(); //resolve stretched widths
{
int totalRowWidth = 0;
@@ -917,8 +909,12 @@ private:
{
RecursiveDcClipper dummy2(dc, rect); //do NOT draw background on cells outside of invalidated rect invalidating foreground text!
+ wxPoint cellAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0))); //client coordinates
+ const int rowHeight = rowLabelWin_.getRowHeight();
+ const auto rowRange = rowLabelWin_.getRowsOnClient(rect); //returns range [begin, end)
+
//draw background lines
- for (int row = rowFirst; row < rowLast; ++row)
+ for (auto row = rowRange.first; row < rowRange.second; ++row)
{
const wxRect rowRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(totalRowWidth, rowHeight));
RecursiveDcClipper dummy3(dc, rowRect);
@@ -932,11 +928,11 @@ private:
return; //done
if (cellAreaTL.x + cw.width_ > rect.x)
- for (int row = rowFirst; row < rowLast; ++row)
+ for (auto row = rowRange.first; row < rowRange.second; ++row)
{
const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight);
RecursiveDcClipper dummy3(dc, cellRect);
- prov->renderCell(dc, cellRect, row, cw.type_, refParent().IsThisEnabled(), drawAsSelected(row));
+ prov->renderCell(dc, cellRect, row, cw.type_, refParent().IsThisEnabled(), drawAsSelected(row), getRowHoverToDraw(row));
}
cellAreaTL.x += cw.width_;
}
@@ -944,6 +940,18 @@ private:
}
}
+ HoverArea getRowHoverToDraw(ptrdiff_t row) const
+ {
+ if (activeSelection)
+ {
+ if (activeSelection->getFirstClick().row_ == row)
+ return activeSelection->getFirstClick().hoverArea_;
+ }
+ else if (highlight.row == row)
+ return highlight.rowHover;
+ return HoverArea::NONE;
+ }
+
bool drawAsSelected(size_t row) const
{
if (activeSelection) //check if user is currently selecting with mouse
@@ -964,14 +972,14 @@ private:
void onMouseLeftDouble(wxMouseEvent& event) override
{
- const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
- const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
- if (row >= 0)
+ if (auto prov = refParent().getDataProvider())
{
- const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x);
- const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE;
+ const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
+ const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position!
+ const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth);
//client is interested in all double-clicks, even those outside of the grid!
- sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType));
+ sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, rowHover));
}
event.Skip();
}
@@ -981,36 +989,37 @@ private:
if (wxWindow::FindFocus() != this) //doesn't seem to happen automatically for right mouse button
SetFocus();
- const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
- const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
+ const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position!
+ assert(row >= 0);
if (row >= 0)
- {
- const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x);
- const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE;
-
- if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area!
+ if (auto prov = refParent().getDataProvider())
{
- if (event.ControlDown())
- activeSelection = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row));
- else if (event.ShiftDown())
- {
- activeSelection = std::make_unique<MouseSelection>(*this, selectionAnchor, true);
- refParent().clearSelection(ALLOW_GRID_EVENT);
- }
- else
+ const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth);
+ GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, rowHover);
+
+ if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area!
{
- activeSelection = std::make_unique<MouseSelection>(*this, row, true);
- refParent().clearSelection(ALLOW_GRID_EVENT);
+ if (event.ControlDown())
+ activeSelection = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row), mouseEvent);
+ else if (event.ShiftDown())
+ {
+ activeSelection = std::make_unique<MouseSelection>(*this, selectionAnchor, true, mouseEvent);
+ refParent().clearSelection(ALLOW_GRID_EVENT);
+ }
+ else
+ {
+ activeSelection = std::make_unique<MouseSelection>(*this, row, true, mouseEvent);
+ refParent().clearSelection(ALLOW_GRID_EVENT);
+ }
}
- }
-
- //notify event *after* potential "clearSelection(true)" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary,
- //then GridClickEvent and the associated GridRangeSelectEvent one after the other
- GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, colType);
- sendEventNow(mouseEvent);
+ //notify event *after* potential "clearSelection(true)" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary,
+ //then GridClickEvent and the associated GridRangeSelectEvent one after the other
+ sendEventNow(mouseEvent);
- Refresh();
- }
+ Refresh();
+ }
event.Skip(); //allow changing focus
}
@@ -1038,19 +1047,25 @@ private:
refParent().selectRangeAndNotify(activeSelection->getStartRow (), //from
activeSelection->getCurrentRow(), //to
- activeSelection->isPositiveSelect());
+ activeSelection->isPositiveSelect(),
+ &activeSelection->getFirstClick());
activeSelection.reset();
}
- //this one may point to row which is not in visible area!
- const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
-
- const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
- const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x);
- const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; //we probably should notify even if colInfo is invalid!
+ if (auto prov = refParent().getDataProvider())
+ {
+ //this one may point to row which is not in visible area!
+ const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
+ const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position!
+ const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth);
+ //notify click event after the range selection! e.g. this makes sure the selection is applied before showing a context menu
+ sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, rowHover));
+ }
- //notify click event after the range selection! e.g. this makes sure the selection is applied before showing a context menu
- sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, colType));
+ //update highlight and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
+ event.SetPosition(ScreenToClient(wxGetMousePosition())); //mouse position may have changed within above callbacks (e.g. context menu was shown)!
+ onMouseMovement(event);
Refresh();
event.Skip(); //allow changing focus
@@ -1059,41 +1074,61 @@ private:
void onMouseCaptureLost(wxMouseCaptureLostEvent& event) override
{
activeSelection.reset();
+ highlight.row = -1;
Refresh();
//event.Skip(); -> we DID handle it!
}
void onMouseMovement(wxMouseEvent& event) override
{
- if (activeSelection)
- activeSelection->evalMousePos(); //eval on both mouse movement + timer event!
-
- //change tooltip
- const std::wstring toolTip = [&]
+ if (auto prov = refParent().getDataProvider())
{
const ptrdiff_t rowCount = refParent().getRowCount();
- const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
+ const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
+ const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position!
+ const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth);
- const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
- const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x);
- if (ct && 0 <= row && row < rowCount)
- if (auto prov = refParent().getDataProvider())
- return prov->getToolTip(row, *ct);
- return std::wstring();
- }();
+ const std::wstring toolTip = [&]
+ {
+ if (cpi.colType != ColumnType::NONE && 0 <= row && row < rowCount)
+ return prov->getToolTip(row, cpi.colType);
+ return std::wstring();
+ }();
+ setToolTip(toolTip); //show even during mouse selection!
+
+ if (activeSelection)
+ activeSelection->evalMousePos(); //call on both mouse movement + timer event!
+ else
+ {
+ refreshHighlight(highlight);
+ highlight.row = row;
+ highlight.rowHover = rowHover;
+ refreshHighlight(highlight); //multiple Refresh() calls are condensed into single one!
+ }
+ }
+ event.Skip();
+ }
- setToolTip(toolTip);
+ void onLeaveWindow(wxMouseEvent& event) override //wxEVT_LEAVE_WINDOW does not respect mouse capture!
+ {
+ if (!activeSelection)
+ {
+ refreshHighlight(highlight);
+ highlight.row = -1;
+ }
event.Skip();
}
+
void onFocus(wxFocusEvent& event) override { Refresh(); event.Skip(); }
class MouseSelection : private wxEvtHandler
{
public:
- MouseSelection(MainWin& wnd, size_t rowStart, bool positiveSelect) :
- wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect)
+ MouseSelection(MainWin& wnd, size_t rowStart, bool positiveSelect, const GridClickEvent& firstClick) :
+ wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect), firstClick_(firstClick)
{
wnd_.CaptureMouse();
timer.Connect(wxEVT_TIMER, wxEventHandler(MouseSelection::onTimer), nullptr, this);
@@ -1105,13 +1140,14 @@ private:
size_t getStartRow () const { return rowStart_; }
size_t getCurrentRow () const { return rowCurrent_; }
bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting?
+ const GridClickEvent& getFirstClick() const { return firstClick_; }
void evalMousePos()
{
double deltaTime = 0;
if (ticksPerSec_ > 0)
{
- const TickVal now = getTicks(); //isValid() on error
+ const TickVal now = getTicks(); //!isValid() on error
deltaTime = static_cast<double>(dist(tickCountLast, now)) / ticksPerSec_; //unit: [sec]
tickCountLast = now;
}
@@ -1146,27 +1182,25 @@ private:
autoScroll(overlapPixX, toScrollX);
autoScroll(overlapPixY, toScrollY);
- if (toScrollX != 0 || toScrollY != 0)
+ if (static_cast<int>(toScrollX) != 0 || static_cast<int>(toScrollY) != 0)
{
wnd_.refParent().scrollDelta(static_cast<int>(toScrollX), static_cast<int>(toScrollY)); //
toScrollX -= static_cast<int>(toScrollX); //rounds down for positive numbers, up for negative,
toScrollY -= static_cast<int>(toScrollY); //exactly what we want
}
- {
- //select current row *after* scrolling
- wxPoint clientPosTrimmed = clientPos;
- numeric::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
-
- const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed);
- const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
- if (newRow >= 0)
- if (rowCurrent_ != newRow)
- {
- rowCurrent_ = newRow;
- wnd_.Refresh();
- }
- }
+ //select current row *after* scrolling
+ wxPoint clientPosTrimmed = clientPos;
+ numeric::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
+
+ const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed);
+ const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ if (newRow >= 0)
+ if (rowCurrent_ != newRow)
+ {
+ rowCurrent_ = newRow;
+ wnd_.Refresh();
+ }
}
private:
@@ -1176,13 +1210,20 @@ private:
const size_t rowStart_;
ptrdiff_t rowCurrent_;
const bool positiveSelect_;
+ const GridClickEvent firstClick_;
wxTimer timer;
- double toScrollX = 0; //count outstanding scroll units to scroll while dragging mouse
+ double toScrollX = 0; //count outstanding scroll unit fractions while dragging mouse
double toScrollY = 0; //
TickVal tickCountLast = getTicks();
const std::int64_t ticksPerSec_ = ticksPerSec();
};
+ struct MouseHighlight
+ {
+ ptrdiff_t row = -1;
+ HoverArea rowHover = HoverArea::NONE;
+ };
+
void ScrollWindow(int dx, int dy, const wxRect* rect) override
{
wxWindow::ScrollWindow(dx, dy, rect);
@@ -1213,10 +1254,26 @@ private:
rowLabelWin_.Update(); //update while dragging scroll thumb
}
+ void refreshRow(size_t row)
+ {
+ const wxRect& rowArea = rowLabelWin_.getRowLabelArea(row); //returns empty rect if row not found
+ const wxPoint topLeft = refParent().CalcScrolledPosition(wxPoint(0, rowArea.y)); //absolute -> client coordinates
+ wxRect cellArea(topLeft, wxSize(refParent().getColWidthsSum(GetClientSize().GetWidth()), rowArea.height));
+ RefreshRect(cellArea, false);
+ }
+
+ void refreshHighlight(const MouseHighlight& hl)
+ {
+ const ptrdiff_t rowCount = refParent().getRowCount();
+ if (0 <= hl.row && hl.row < rowCount && hl.rowHover != HoverArea::NONE) //no highlight? => NOP!
+ refreshRow(hl.row);
+ }
+
RowLabelWin& rowLabelWin_;
ColLabelWin& colLabelWin_;
std::unique_ptr<MouseSelection> activeSelection; //bound while user is selecting with mouse
+ MouseHighlight highlight; //current mouse highlight (superseeded by activeSelection if available)
ptrdiff_t cursorRow = 0;
size_t selectionAnchor = 0;
@@ -1427,14 +1484,10 @@ void Grid::onKeyDown(wxKeyEvent& event)
int keyCode = event.GetKeyCode();
if (GetLayoutDirection() == wxLayout_RightToLeft)
{
- if (keyCode == WXK_LEFT)
+ if (keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT)
keyCode = WXK_RIGHT;
- else if (keyCode == WXK_RIGHT)
+ else if (keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT)
keyCode = WXK_LEFT;
- else if (keyCode == WXK_NUMPAD_LEFT)
- keyCode = WXK_NUMPAD_RIGHT;
- else if (keyCode == WXK_NUMPAD_RIGHT)
- keyCode = WXK_NUMPAD_LEFT;
}
const ptrdiff_t rowCount = getRowCount();
@@ -1547,7 +1600,7 @@ void Grid::onKeyDown(wxKeyEvent& event)
case 'A': //Ctrl + A - select all
if (event.ControlDown())
- selectRangeAndNotify(0, rowCount);
+ selectRangeAndNotify(0, rowCount, true /*positive*/, nullptr /*mouseInitiated*/);
break;
case WXK_NUMPAD_ADD: //CTRL + '+' - auto-size all
@@ -1581,7 +1634,7 @@ void Grid::selectAllRows(GridEventPolicy rangeEventPolicy)
if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction
{
- GridRangeSelectEvent selEvent(0, getRowCount(), true);
+ GridRangeSelectEvent selEvent(0, getRowCount(), true, nullptr);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(selEvent);
}
@@ -1595,7 +1648,7 @@ void Grid::clearSelection(GridEventPolicy rangeEventPolicy)
if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction
{
- GridRangeSelectEvent unselectionEvent(0, getRowCount(), false);
+ GridRangeSelectEvent unselectionEvent(0, getRowCount(), false, nullptr);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(unselectionEvent);
}
@@ -1645,7 +1698,7 @@ void Grid::Refresh(bool eraseBackground, const wxRect* rect)
updateWindowSizes();
}
- if (selection.size() != rowCountNew) //clear selection only when needed (consider setSelectedRows())
+ if (selection.maxSize() != rowCountNew) //clear selection only when needed (consider setSelectedRows())
selection.init(rowCountNew);
wxScrolledWindow::Refresh(eraseBackground, rect);
@@ -1667,8 +1720,11 @@ void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr)
std::vector<VisibleColumn> visCols;
for (const ColumnAttribute& ca : attr)
+ {
+ assert(ca.type_ != ColumnType::NONE);
if (ca.visible_)
visCols.emplace_back(ca.type_, ca.offset_, ca.stretch_);
+ }
//"ownership" of visible columns is now within Grid
visibleCols = visCols;
@@ -1742,34 +1798,16 @@ void Grid::showScrollBars(Grid::ScrollBarStatus horizontal, Grid::ScrollBarStatu
#endif
updateWindowSizes();
+}
+#if defined ZEN_WIN || defined ZEN_MAC
+void Grid::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh)
+{
/*
wxWidgets >= 2.9 ShowScrollbars() is next to useless since it doesn't
honor wxSHOW_SB_ALWAYS on OS X, so let's ditch it and avoid more non-portability surprises
-
- #if wxCHECK_VERSION(2, 9, 0)
- auto mapStatus = [](ScrollBarStatus sbStatus) -> wxScrollbarVisibility
- {
- switch (sbStatus)
- {
- case SB_SHOW_AUTOMATIC:
- return wxSHOW_SB_DEFAULT;
- case SB_SHOW_ALWAYS:
- return wxSHOW_SB_ALWAYS;
- case SB_SHOW_NEVER:
- return wxSHOW_SB_NEVER;
- }
- assert(false);
- return wxSHOW_SB_DEFAULT;
- };
- ShowScrollbars(mapStatus(horizontal), mapStatus(vertical));
- #endif
*/
-}
-#if defined ZEN_WIN || defined ZEN_MAC
-void Grid::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh)
-{
ScrollBarStatus sbStatus = SB_SHOW_AUTOMATIC;
if (orientation == wxHORIZONTAL)
sbStatus = showScrollbarX;
@@ -1806,24 +1844,6 @@ wxWindow& Grid::getMainWin () { return *mainWin_; }
const wxWindow& Grid::getMainWin() const { return *mainWin_; }
-wxRect Grid::getColumnLabelArea(ColumnType colType) const
-{
- std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths
-
- auto iterCol = std::find_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; });
- if (iterCol != absWidths.end())
- {
- ptrdiff_t posX = 0;
- for (auto it = absWidths.begin(); it != iterCol; ++it)
- posX += it->width_;
-
- return wxRect(wxPoint(posX, 0), wxSize(iterCol->width_, colLabelHeight));
- }
-
- return wxRect();
-}
-
-
Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
{
const int absPosX = CalcUnscrolledPosition(pos).x;
@@ -1862,7 +1882,7 @@ void Grid::moveColumn(size_t colFrom, size_t colTo)
colTo < visibleCols.size() &&
colTo != colFrom)
{
- const auto colAtt = visibleCols[colFrom];
+ const VisibleColumn colAtt = visibleCols[colFrom];
visibleCols.erase (visibleCols.begin() + colFrom);
visibleCols.insert(visibleCols.begin() + colTo, colAtt);
}
@@ -1871,35 +1891,35 @@ void Grid::moveColumn(size_t colFrom, size_t colTo)
ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos) const
{
- std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths
const int absPosX = CalcUnscrolledPosition(pos).x;
- int accuWidth = 0;
- for (auto iterCol = absWidths.begin(); iterCol != absWidths.end(); ++iterCol)
+ int accWidth = 0;
+ std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths
+ for (auto itCol = absWidths.begin(); itCol != absWidths.end(); ++itCol)
{
- const int width = iterCol->width_; //beware dreaded unsigned conversions!
- accuWidth += width;
+ const int width = itCol->width_; //beware dreaded unsigned conversions!
+ accWidth += width;
- if (absPosX < accuWidth - width / 2)
- return iterCol - absWidths.begin();
+ if (absPosX < accWidth - width / 2)
+ return itCol - absWidths.begin();
}
return absWidths.size();
}
-Opt<ColumnType> Grid::colToType(size_t col) const
+ColumnType Grid::colToType(size_t col) const
{
if (col < visibleCols.size())
return visibleCols[col].type_;
- return NoValue();
+ return ColumnType::NONE;
}
ptrdiff_t Grid::getRowAtPos(int posY) const { return rowLabelWin_->getRowAtPos(posY); }
-Opt<ColumnType> Grid::getColumnAtPos(int posX) const
+Grid::ColumnPosInfo Grid::getColumnAtPos(int posX) const
{
if (posX >= 0)
{
@@ -1908,18 +1928,44 @@ Opt<ColumnType> Grid::getColumnAtPos(int posX) const
{
accWidth += cw.width_;
if (posX < accWidth)
- return cw.type_;
+ return { cw.type_, posX + cw.width_ - accWidth, cw.width_ };
}
}
- return NoValue();
+ return { ColumnType::NONE, 0, 0 };
}
-wxRect Grid::getCellArea(size_t row, ColumnType colType) const
+wxRect Grid::getColumnLabelArea(ColumnType colType) const
{
- const wxRect& colArea = getColumnLabelArea(colType);
- const wxRect& rowArea = rowLabelWin_->getRowLabelArea(row);
- return wxRect(wxPoint(colArea.x, rowArea.y), wxSize(colArea.width, rowArea.height));
+ std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths
+
+ //colType is not unique in general, but *this* function expects it!
+ assert(std::count_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; }) <= 1);
+
+ auto itCol = std::find_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; });
+ if (itCol != absWidths.end())
+ {
+ ptrdiff_t posX = 0;
+ for (auto it = absWidths.begin(); it != itCol; ++it)
+ posX += it->width_;
+
+ return wxRect(wxPoint(posX, 0), wxSize(itCol->width_, colLabelHeight));
+ }
+ return wxRect();
+}
+
+
+void Grid::refreshCell(size_t row, ColumnType colType)
+{
+ const wxRect& colArea = getColumnLabelArea(colType); //returns empty rect if column not found
+ const wxRect& rowArea = rowLabelWin_->getRowLabelArea(row); //returns empty rect if row not found
+ if (colArea.height > 0 && rowArea.height > 0)
+ {
+ const wxPoint topLeft = CalcScrolledPosition(wxPoint(colArea.x, rowArea.y)); //absolute -> client coordinates
+ const wxRect cellArea(topLeft, wxSize(colArea.width, rowArea.height));
+
+ getMainWin().RefreshRect(cellArea, false);
+ }
}
@@ -1929,7 +1975,7 @@ void Grid::setGridCursor(size_t row)
makeRowVisible(row);
selection.clear(); //clear selection, do NOT fire event
- selectRangeAndNotify(row, row); //set new selection + fire event
+ selectRangeAndNotify(row, row, true /*positive*/, nullptr /*mouseInitiated*/); //set new selection + fire event
mainWin_->Refresh();
rowLabelWin_->Refresh(); //row labels! (Kubuntu)
@@ -1944,7 +1990,7 @@ void Grid::selectWithCursor(ptrdiff_t row)
makeRowVisible(row);
selection.clear(); //clear selection, do NOT fire event
- selectRangeAndNotify(anchorRow, row); //set new selection + fire event
+ selectRangeAndNotify(anchorRow, row, true /*positive*/, nullptr /*mouseInitiated*/); //set new selection + fire event
mainWin_->Refresh();
rowLabelWin_->Refresh();
@@ -1953,7 +1999,7 @@ void Grid::selectWithCursor(ptrdiff_t row)
void Grid::makeRowVisible(size_t row)
{
- const wxRect labelRect = rowLabelWin_->getRowLabelArea(row); //returns empty rect if column not found
+ const wxRect labelRect = rowLabelWin_->getRowLabelArea(row); //returns empty rect if row not found
if (labelRect.height > 0)
{
int scrollPosX = 0;
@@ -1966,16 +2012,16 @@ void Grid::makeRowVisible(size_t row)
const int clientPosY = CalcScrolledPosition(labelRect.GetTopLeft()).y;
if (clientPosY < 0)
{
- const int scrollPosY = labelRect.GetTopLeft().y / pixelsPerUnitY;
+ const int scrollPosY = labelRect.y / pixelsPerUnitY;
Scroll(scrollPosX, scrollPosY);
updateWindowSizes(); //may show horizontal scroll bar
}
- else if (clientPosY + labelRect.GetHeight() > rowLabelWin_->GetClientSize().GetHeight())
+ else if (clientPosY + labelRect.height > rowLabelWin_->GetClientSize().GetHeight())
{
auto execScroll = [&](int clientHeight)
{
- const int scrollPosY = std::ceil((labelRect.GetTopLeft().y - clientHeight +
- labelRect.GetHeight()) / static_cast<double>(pixelsPerUnitY));
+ const int scrollPosY = std::ceil((labelRect.y - clientHeight +
+ labelRect.height) / static_cast<double>(pixelsPerUnitY));
Scroll(scrollPosX, scrollPosY);
updateWindowSizes(); //may show horizontal scroll bar
};
@@ -1992,7 +2038,7 @@ void Grid::makeRowVisible(size_t row)
}
-void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive)
+void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const GridClickEvent* mouseInitiated)
{
//sort + convert to half-open range
auto rowFirst = std::min(rowFrom, rowTo);
@@ -2005,7 +2051,7 @@ void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positiv
selection.selectRange(rowFirst, rowLast, positive);
//notify event
- GridRangeSelectEvent selectionEvent(rowFirst, rowLast, positive);
+ GridRangeSelectEvent selectionEvent(rowFirst, rowLast, positive, mouseInitiated);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(selectionEvent);
@@ -2015,14 +2061,14 @@ void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positiv
void Grid::scrollTo(size_t row)
{
- const wxRect labelRect = rowLabelWin_->getRowLabelArea(row); //returns empty rect if column not found
+ const wxRect labelRect = rowLabelWin_->getRowLabelArea(row); //returns empty rect if row not found
if (labelRect.height > 0)
{
int pixelsPerUnitY = 0;
GetScrollPixelsPerUnit(nullptr, &pixelsPerUnitY);
if (pixelsPerUnitY > 0)
{
- const int scrollPosYNew = labelRect.GetTopLeft().y / pixelsPerUnitY;
+ const int scrollPosYNew = labelRect.y / pixelsPerUnitY;
int scrollPosXOld = 0;
int scrollPosYOld = 0;
GetViewStart(&scrollPosXOld, &scrollPosYOld);
diff --git a/wx+/grid.h b/wx+/grid.h
index e1205092..cba86a24 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -18,13 +18,10 @@
namespace zen
{
-typedef enum { DUMMY_COLUMN_TYPE = static_cast<unsigned int>(-1) } ColumnType;
-
-//----- events ------------------------------------------------------------------------
-extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_LEFT; //generates: GridClickEvent
-extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_RIGHT; //
-extern const wxEventType EVENT_GRID_COL_RESIZE; //generates: GridColumnResizeEvent
+enum class ColumnType { NONE = -1 }; //user-defiend column type
+enum class HoverArea { NONE = -1 }; //user-defined area for mouse selections for a given row (may span multiple columns or split a single column into multiple areas)
+//------------------------ events ------------------------------------------------
extern const wxEventType EVENT_GRID_MOUSE_LEFT_DOUBLE; //
extern const wxEventType EVENT_GRID_MOUSE_LEFT_DOWN; //
extern const wxEventType EVENT_GRID_MOUSE_LEFT_UP; //generates: GridClickEvent
@@ -34,48 +31,63 @@ extern const wxEventType EVENT_GRID_MOUSE_RIGHT_UP; //
extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridRangeSelectEvent
//NOTE: neither first nor second row need to match EVENT_GRID_MOUSE_LEFT_DOWN/EVENT_GRID_MOUSE_LEFT_UP: user holding SHIFT; moving out of window...
+extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_LEFT; //generates: GridLabelClickEvent
+extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_RIGHT; //
+extern const wxEventType EVENT_GRID_COL_RESIZE; //generates: GridColumnResizeEvent
+
//example: wnd.Connect(EVENT_GRID_COL_LABEL_LEFT_CLICK, GridClickEventHandler(MyDlg::OnLeftClick), nullptr, this);
struct GridClickEvent : public wxMouseEvent
{
- GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, ColumnType colType) : wxMouseEvent(me), row_(row), colType_(colType) { SetEventType(et); }
+ GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, HoverArea hoverArea) :
+ wxMouseEvent(me), row_(row), hoverArea_(hoverArea) { SetEventType(et); }
wxEvent* Clone() const override { return new GridClickEvent(*this); }
const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range
- const ColumnType colType_; //may be DUMMY_COLUMN_TYPE
-};
-
-struct GridColumnResizeEvent : public wxCommandEvent
-{
- GridColumnResizeEvent(int offset, ColumnType colType) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), offset_(offset) {}
- wxEvent* Clone() const override { return new GridColumnResizeEvent(*this); }
-
- const ColumnType colType_;
- const int offset_;
+ const HoverArea hoverArea_; //may be HoverArea::NONE
};
struct GridRangeSelectEvent : public wxCommandEvent
{
- GridRangeSelectEvent(size_t rowFirst, size_t rowLast, bool positive) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), positive_(positive), rowFirst_(rowFirst), rowLast_(rowLast) { assert(rowFirst <= rowLast); }
+ GridRangeSelectEvent(size_t rowFirst, size_t rowLast, bool positive, const GridClickEvent* mouseInitiated) :
+ wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), positive_(positive),
+ mouseInitiated_(mouseInitiated ? *mouseInitiated : Opt<GridClickEvent>()) { assert(rowFirst <= rowLast); }
wxEvent* Clone() const override { return new GridRangeSelectEvent(*this); }
- const bool positive_; //"false" when clearing selection!
const size_t rowFirst_; //selected range: [rowFirst_, rowLast_)
const size_t rowLast_;
+ const bool positive_; //"false" when clearing selection!
+ Opt<GridClickEvent> mouseInitiated_; //filled unless selection was performed via keyboard shortcuts or is result of Grid::clearSelection()
+};
+
+struct GridLabelClickEvent : public wxMouseEvent
+{
+ GridLabelClickEvent(wxEventType et, const wxMouseEvent& me, ColumnType colType) : wxMouseEvent(me), colType_(colType) { SetEventType(et); }
+ wxEvent* Clone() const override { return new GridLabelClickEvent(*this); }
+
+ const ColumnType colType_; //may be ColumnType::NONE
};
-typedef void (wxEvtHandler::*GridClickEventFunction )(GridClickEvent&);
-typedef void (wxEvtHandler::*GridColumnResizeEventFunction)(GridColumnResizeEvent&);
-typedef void (wxEvtHandler::*GridRangeSelectEventFunction )(GridRangeSelectEvent&);
-#define GridClickEventHandler(func) \
- (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridClickEventFunction, &func)
+struct GridColumnResizeEvent : public wxCommandEvent
+{
+ GridColumnResizeEvent(int offset, ColumnType colType) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), offset_(offset) {}
+ wxEvent* Clone() const override { return new GridColumnResizeEvent(*this); }
+
+ const ColumnType colType_;
+ const int offset_;
+};
+
+using GridClickEventFunction = void (wxEvtHandler::*)(GridClickEvent&);
+using GridRangeSelectEventFunction = void (wxEvtHandler::*)(GridRangeSelectEvent&);
+using GridLabelClickEventFunction = void (wxEvtHandler::*)(GridLabelClickEvent&);
+using GridColumnResizeEventFunction = void (wxEvtHandler::*)(GridColumnResizeEvent&);
-#define GridColumnResizeEventHandler(func) \
- (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridColumnResizeEventFunction, &func)
+#define GridClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridClickEventFunction, &func)
+#define GridRangeSelectEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridRangeSelectEventFunction, &func)
+#define GridLabelClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridLabelClickEventFunction, &func)
+#define GridColumnResizeEventHandler(func)(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridColumnResizeEventFunction, &func)
-#define GridRangeSelectEventHandler(func) \
- (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridRangeSelectEventFunction, &func)
//------------------------------------------------------------------------------------------------------------
class Grid;
wxColor getColorSelectionGradientFrom();
@@ -90,12 +102,13 @@ public:
virtual size_t getRowCount() const = 0;
- //grid area
+ //cell area
virtual std::wstring getValue(size_t row, ColumnType colType) const = 0;
virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation
- virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected); //
- virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType ); //must correspond to renderCell()!
+ virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected, HoverArea rowHover);
+ virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType); //must correspond to renderCell()!
virtual std::wstring getToolTip (size_t row, ColumnType colType) const { return std::wstring(); }
+ virtual HoverArea getRowMouseHover(size_t row, ColumnType colType, int cellRelativePosX, int cellWidth) { return HoverArea::NONE; }
//label area
virtual std::wstring getColumnLabel(ColumnType colType) const = 0;
@@ -105,15 +118,16 @@ public:
static const int COLUMN_GAP_LEFT; //for left-aligned text
protected: //optional helper routines
- static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
- static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor);
- static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
+ static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor);
+ static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
- static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
- static void drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted);
- static void drawColumnLabelText (wxDC& dc, const wxRect& rect, const std::wstring& text);
+ static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
+ static void drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted);
+ static void drawColumnLabelText (wxDC& dc, const wxRect& rect, const std::wstring& text);
};
+
enum GridEventPolicy
{
ALLOW_GRID_EVENT,
@@ -179,9 +193,16 @@ public:
const wxWindow& getMainWin() const;
ptrdiff_t getRowAtPos(int posY) const; //return -1 for invalid position, >= rowCount if out of range; absolute coordinates!
- Opt<ColumnType> getColumnAtPos(int posX) const;
- wxRect getCellArea(size_t row, ColumnType colType) const; //returns empty rect if column not found; absolute coordinates!
+ struct ColumnPosInfo
+ {
+ ColumnType colType; //ColumnType::NONE no column at x position!
+ int cellRelativePosX;
+ int colWidth;
+ };
+ ColumnPosInfo getColumnAtPos(int posX) const; //absolute position!
+
+ void refreshCell(size_t row, ColumnType colType);
void enableColumnMove (bool value) { allowColumnMove = value; }
void enableColumnResize(bool value) { allowColumnResize = value; }
@@ -231,7 +252,7 @@ private:
public:
void init(size_t rowCount) { rowSelectionValue.resize(rowCount); clear(); }
- size_t size() const { return rowSelectionValue.size(); }
+ size_t maxSize() const { return rowSelectionValue.size(); }
std::vector<size_t> get() const
{
@@ -294,7 +315,7 @@ private:
wxRect getColumnLabelArea(ColumnType colType) const; //returns empty rect if column not found
- void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive = true); //select inclusive range [rowFrom, rowTo] + notify event!
+ void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const GridClickEvent* mouseInitiated); //select inclusive range [rowFrom, rowTo] + notify event!
bool isSelected(size_t row) const { return selection.isSelected(row); }
@@ -307,7 +328,7 @@ private:
void moveColumn(size_t colFrom, size_t colTo);
ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos) const; //return < 0 on error
- Opt<ColumnType> colToType(size_t col) const;
+ ColumnType colToType(size_t col) const; //returns ColumnType::NONE on error
/*
Visual layout:
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index 98cdc7a8..4ba62bb9 100644
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -50,12 +50,18 @@ public:
}
void init(const Zstring& filepath);
+ void cleanup()
+ {
+ bitmaps.clear();
+ anims.clear();
+ }
const wxBitmap& getImage (const wxString& name) const;
const wxAnimation& getAnimation(const wxString& name) const;
private:
GlobalResources() {}
+ ~GlobalResources() { assert(bitmaps.empty() && anims.empty()); } //don't leave wxWidgets objects for static destruction!
GlobalResources (const GlobalResources&) = delete;
GlobalResources& operator=(const GlobalResources&) = delete;
@@ -127,6 +133,7 @@ const wxAnimation& GlobalResources::getAnimation(const wxString& name) const
void zen::initResourceImages(const Zstring& filepath) { GlobalResources::instance().init(filepath); }
+void zen::cleanupResourceImages() { GlobalResources::instance().cleanup(); }
const wxBitmap& zen::getResourceImage(const wxString& name) { return GlobalResources::instance().getImage(name); }
diff --git a/wx+/image_resources.h b/wx+/image_resources.h
index 30f89848..61623614 100644
--- a/wx+/image_resources.h
+++ b/wx+/image_resources.h
@@ -14,6 +14,7 @@
namespace zen
{
void initResourceImages(const Zstring& filepath); //pass resources .zip file at application startup
+void cleanupResourceImages();
const wxBitmap& getResourceImage (const wxString& name);
const wxAnimation& getResourceAnimation(const wxString& name);
diff --git a/wx+/image_tools.h b/wx+/image_tools.h
index 5e99653c..554e7b49 100644
--- a/wx+/image_tools.h
+++ b/wx+/image_tools.h
@@ -50,7 +50,7 @@ void convertToVanillaImage(wxImage& img); //add alpha channel if missing + remov
//wxColor gradient(const wxColor& from, const wxColor& to, double fraction); //maps fraction within [0, 1] to an intermediate color
-//wxColour hsvColor(double h, double s, double v); //h within [0, 360), s, v within [0, 1]
+//wxColor hsvColor(double h, double s, double v); //h within [0, 360), s, v within [0, 1]
@@ -208,7 +208,7 @@ wxColor gradient(const wxColor& from, const wxColor& to, double fraction)
/*
inline
-wxColour hsvColor(double h, double s, double v) //h within [0, 360), s, v within [0, 1]
+wxColor hsvColor(double h, double s, double v) //h within [0, 360), s, v within [0, 1]
{
//http://de.wikipedia.org/wiki/HSV-Farbraum
@@ -238,17 +238,17 @@ wxColour hsvColor(double h, double s, double v) //h within [0, 360), s, v within
switch (h_i)
{
case 0:
- return wxColour(vi, t, p);
+ return wxColor(vi, t, p);
case 1:
- return wxColour(q, vi, p);
+ return wxColor(q, vi, p);
case 2:
- return wxColour(p, vi, t);
+ return wxColor(p, vi, t);
case 3:
- return wxColour(p, q, vi);
+ return wxColor(p, q, vi);
case 4:
- return wxColour(t, p, vi);
+ return wxColor(t, p, vi);
case 5:
- return wxColour(vi, p, q);
+ return wxColor(vi, p, q);
}
assert(false);
return *wxBLACK;
diff --git a/wx+/tooltip.h b/wx+/tooltip.h
index 8ddba819..06953361 100644
--- a/wx+/tooltip.h
+++ b/wx+/tooltip.h
@@ -15,8 +15,7 @@ namespace zen
class Tooltip
{
public:
- Tooltip(wxWindow& parent) : //parent needs to live at least as long as this instance!
- tipWindow(nullptr), parent_(parent) {}
+ Tooltip(wxWindow& parent) : parent_(parent) {} //parent needs to live at least as long as this instance!
void show(const wxString& text,
wxPoint mousePos, //absolute screen coordinates
@@ -25,7 +24,7 @@ public:
private:
class TooltipDialogGenerated;
- TooltipDialogGenerated* tipWindow;
+ TooltipDialogGenerated* tipWindow = nullptr;
wxWindow& parent_;
};
}
bgstack15