Commit 1b49a894 authored by twanvl's avatar twanvl

Added scrollbar for DropDownList, fixes #52

parent ed92518f
...@@ -149,8 +149,8 @@ void GalleryList::RefreshSelection() { ...@@ -149,8 +149,8 @@ void GalleryList::RefreshSelection() {
} }
void GalleryList::onScroll(wxScrollWinEvent& ev) { void GalleryList::onScroll(wxScrollWinEvent& ev) {
wxEventType type = ev.GetEventType(); wxEventType type = ev.GetEventType();
if (type == wxEVT_SCROLLWIN_TOP) { if (type == wxEVT_SCROLLWIN_TOP) {
scrollTo(0); scrollTo(0);
} else if (type == wxEVT_SCROLLWIN_BOTTOM) { } else if (type == wxEVT_SCROLLWIN_BOTTOM) {
scrollTo(INT_MAX); scrollTo(INT_MAX);
...@@ -164,7 +164,7 @@ void GalleryList::onScroll(wxScrollWinEvent& ev) { ...@@ -164,7 +164,7 @@ void GalleryList::onScroll(wxScrollWinEvent& ev) {
scrollTo(visibleEnd() - mainSize(item_size)); scrollTo(visibleEnd() - mainSize(item_size));
} else { } else {
scrollTo(ev.GetPosition()); scrollTo(ev.GetPosition());
} }
} }
void GalleryList::onSize(wxSizeEvent& ev) { void GalleryList::onSize(wxSizeEvent& ev) {
......
...@@ -56,7 +56,7 @@ class DropDownHider : public wxEvtHandler { ...@@ -56,7 +56,7 @@ class DropDownHider : public wxEvtHandler {
// ----------------------------------------------------------------------------- : DropDownList : Show/Hide // ----------------------------------------------------------------------------- : DropDownList : Show/Hide
DropDownList::DropDownList(Window* parent, bool is_submenu, ValueViewer* viewer) DropDownList::DropDownList(Window* parent, bool is_submenu, ValueViewer* viewer)
: wxPopupWindow(parent) : wxPopupWindow(parent, wxSIMPLE_BORDER)
, text_offset(1) , text_offset(1)
, item_size(100,1) , item_size(100,1)
, icon_size(0,0) , icon_size(0,0)
...@@ -106,10 +106,14 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) { ...@@ -106,10 +106,14 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) {
int line_count = 0; int line_count = 0;
for (size_t i = 0 ; i < count ; ++i) if (lineBelow(i)) line_count += 1; for (size_t i = 0 ; i < count ; ++i) if (lineBelow(i)) line_count += 1;
// size // size
RealSize border_size(2,2); // GetClientSize() - GetSize(), assume 1px borders
RealSize size( RealSize size(
item_size.width + marginW * 2, item_size.width + marginW * 2,
item_size.height * count + marginH * 2 + line_count item_size.height * count + marginH * 2 + line_count
); );
RealSize virtual_size = size;
SetVirtualSize(virtual_size);
// placement
int parent_height = 0; int parent_height = 0;
if (!in_place && viewer) { if (!in_place && viewer) {
// Position the drop down list below the editor control (based on the style) // Position the drop down list below the editor control (based on the style)
...@@ -128,10 +132,23 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) { ...@@ -128,10 +132,23 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) {
parent_height = -(int)item_size.height - 1; parent_height = -(int)item_size.height - 1;
} }
pos = GetParent()->ClientToScreen(pos); pos = GetParent()->ClientToScreen(pos);
// is there enough room for all items, or do we need a scrollbar?
int room_below = wxGetDisplaySize().y - border_size.height - pos.y - parent_height - 50;
int max_height = max(200, room_below);
if (size.height > max_height) {
size.height = max_height;
size.width += wxSystemSettings::GetMetric(wxSYS_VSCROLL_ARROW_X); // width of scrollbar
SetScrollbar(wxVERTICAL, 0, size.height, virtual_size.height, false);
} else {
SetScrollbar(wxVERTICAL,0,0,0,false);
}
// move & resize // move & resize
item_size.width = size.width - marginW * 2; item_size.width = virtual_size.width - marginW * 2;
SetSize(size); SetSize(add_diagonal(size, border_size));
Position(pos, wxSize(0, parent_height)); Position(pos, wxSize(0, parent_height));
// visible item
visible_start = 0;
ensureSelectedItemVisible();
// set event handler // set event handler
if (hider) { if (hider) {
assert(hider2); assert(hider2);
...@@ -217,12 +234,40 @@ bool DropDownList::showSubMenu(size_t item, int y) { ...@@ -217,12 +234,40 @@ bool DropDownList::showSubMenu(size_t item, int y) {
return true; return true;
} }
bool DropDownList::selectItem(size_t item) {
if ((int)item >= 0 && item < itemCount()) {
selected_item = item;
ensureSelectedItemVisible();
Refresh(false);
return true;
} else {
return false;
}
}
void DropDownList::ensureSelectedItemVisible() {
if (selected_item == NO_SELECTION) return;
// ensure that this item is visible
int item_top = itemPosition(selected_item);
wxSize cs = GetClientSize();
if (item_top < marginH) {
scrollTo(item_top + visible_start);
} else if (item_top + item_size.height - 1 > cs.y - marginH) {
scrollTo(item_top + visible_start + item_size.height - 1 - (cs.y - marginH));
}
}
void DropDownList::scrollTo(int pos) {
visible_start = max(0, min(GetVirtualSize().y - GetSize().y, pos));
SetScrollPos(wxVERTICAL, visible_start);
Refresh(false);
}
int DropDownList::itemPosition(size_t item) const { int DropDownList::itemPosition(size_t item) const {
int y = marginH; int y = marginH - visible_start;
size_t count = itemCount(); size_t count = itemCount();
for (size_t i = 0 ; i < count ; ++i) { for (size_t i = 0 ; i < count ; ++i) {
if (i == item) return y; if (i == item) return y;
y += (int)item_size.height + lineBelow(item); y += (int)item_size.height + lineBelow(i);
} }
// not found // not found
assert(false); assert(false);
...@@ -251,14 +296,14 @@ void DropDownList::onPaint(wxPaintEvent&) { ...@@ -251,14 +296,14 @@ void DropDownList::onPaint(wxPaintEvent&) {
void DropDownList::draw(DC& dc) { void DropDownList::draw(DC& dc) {
// Size // Size
wxSize cs = GetClientSize(); wxSize cs = dc.GetSize();
// Draw background & frame // Draw background & frame
dc.SetPen (wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWFRAME)); dc.SetPen (*wxTRANSPARENT_PEN);
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.DrawRectangle(0, 0, cs.GetWidth(), cs.GetHeight()); dc.DrawRectangle(0, 0, cs.x, cs.y);
dc.SetFont(*wxNORMAL_FONT); dc.SetFont(*wxNORMAL_FONT);
// Draw items // Draw items
int y = marginH; int y = marginH - visible_start;
size_t count = itemCount(); size_t count = itemCount();
for (size_t i = 0 ; i < count ; ++i) { for (size_t i = 0 ; i < count ; ++i) {
drawItem(dc, y, i); drawItem(dc, y, i);
...@@ -267,6 +312,7 @@ void DropDownList::draw(DC& dc) { ...@@ -267,6 +312,7 @@ void DropDownList::draw(DC& dc) {
} }
void DropDownList::drawItem(DC& dc, int y, size_t item) { void DropDownList::drawItem(DC& dc, int y, size_t item) {
if (y + item_size.height <= 0 || y >= dc.GetSize().y) return; // not visible
// draw background // draw background
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
if (item == selected_item) { if (item == selected_item) {
...@@ -328,21 +374,21 @@ void DropDownList::onLeftUp(wxMouseEvent&) { ...@@ -328,21 +374,21 @@ void DropDownList::onLeftUp(wxMouseEvent&) {
void DropDownList::onMotion(wxMouseEvent& ev) { void DropDownList::onMotion(wxMouseEvent& ev) {
// size // size
wxSize cs = GetClientSize(); wxSize cs = GetClientSize();
// find selected item // inside?
if (ev.GetX() < marginW || ev.GetX() + marginW >= cs.GetWidth() || ev.GetY() < marginH || ev.GetY() + marginH >= cs.GetHeight()) { if (ev.GetX() < marginW || ev.GetX() + marginW >= cs.GetWidth() || ev.GetY() < marginH || ev.GetY() + marginH >= cs.GetHeight()) {
ev.Skip(); ev.Skip();
return; return;
} }
int startY = marginH; // find selected item
int startY = marginH - visible_start;
size_t count = itemCount(); size_t count = itemCount();
for (size_t i = 0 ; i < count ; ++i) { for (size_t i = 0 ; i < count ; ++i) {
int endY = startY + (int)item_size.height; int endY = startY + (int)item_size.height;
if (ev.GetY() >= startY && ev.GetY() < endY) { if (ev.GetY() >= startY && ev.GetY() < endY) {
selected_item = i;
if (itemEnabled(i)) { if (itemEnabled(i)) {
showSubMenu(i, startY); showSubMenu(i, startY);
} }
Refresh(false); selectItem(i);
return; return;
} }
startY = endY + lineBelow(i); startY = endY + lineBelow(i);
...@@ -361,6 +407,29 @@ void DropDownList::onMouseLeave(wxMouseEvent& ev) { ...@@ -361,6 +407,29 @@ void DropDownList::onMouseLeave(wxMouseEvent& ev) {
} }
} }
void DropDownList::onMouseWheel(wxMouseEvent& ev) {
scrollTo(visible_start - item_size.height * ev.GetWheelRotation() / ev.GetWheelDelta());
}
void DropDownList::onScroll(wxScrollWinEvent& ev) {
wxEventType type = ev.GetEventType();
if (type == wxEVT_SCROLLWIN_TOP) {
scrollTo(0);
} else if (type == wxEVT_SCROLLWIN_BOTTOM) {
scrollTo(INT_MAX);
} else if (type == wxEVT_SCROLLWIN_LINEUP) {
scrollTo(visible_start - item_size.height);
} else if (type == wxEVT_SCROLLWIN_LINEDOWN) {
scrollTo(visible_start + item_size.height);
} else if (type == wxEVT_SCROLLWIN_PAGEUP) {
scrollTo(visible_start - (GetClientSize().y - item_size.height));
} else if (type == wxEVT_SCROLLWIN_PAGEDOWN) {
scrollTo(visible_start + (GetClientSize().y - item_size.height));
} else {
scrollTo(ev.GetPosition());
}
}
// ----------------------------------------------------------------------------- : DropDownList : Parent events // ----------------------------------------------------------------------------- : DropDownList : Parent events
bool DropDownList::onMouseInParent(wxMouseEvent& ev, bool open_in_place) { bool DropDownList::onMouseInParent(wxMouseEvent& ev, bool open_in_place) {
...@@ -379,19 +448,9 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) { ...@@ -379,19 +448,9 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) {
} else { } else {
switch (k) { switch (k) {
case WXK_UP: case WXK_UP:
if (selected_item > 0) { return selectItem(selected_item - 1);
selected_item -= 1;
Refresh(false);
return true;
}
break;
case WXK_DOWN: case WXK_DOWN:
if (selected_item + 1 < itemCount()) { return selectItem(selected_item + 1);
selected_item += 1;
Refresh(false);
return true;
}
break;
case WXK_RETURN: case WXK_RETURN:
if (!showSubMenu() && (selected_item == NO_SELECTION || itemEnabled(selected_item))) { if (!showSubMenu() && (selected_item == NO_SELECTION || itemEnabled(selected_item))) {
hide(true, false); // don't veto; always close hide(true, false); // don't veto; always close
...@@ -408,6 +467,8 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) { ...@@ -408,6 +467,8 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) {
case WXK_LEFT: case WXK_LEFT:
if (parent_menu) hide(false); if (parent_menu) hide(false);
break; break;
case WXK_RIGHT:
return showSubMenu();
default: default:
// match first character of an item, start searching just after the current selection // match first character of an item, start searching just after the current selection
size_t si = selected_item != NO_SELECTION ? selected_item + 1 : 0; size_t si = selected_item != NO_SELECTION ? selected_item + 1 : 0;
...@@ -424,7 +485,7 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) { ...@@ -424,7 +485,7 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) {
// first character matches // first character matches
selected_item = index; selected_item = index;
showSubMenu(); showSubMenu();
Refresh(false); selectItem(index);
return true; return true;
} }
} }
...@@ -448,4 +509,6 @@ BEGIN_EVENT_TABLE(DropDownList,wxPopupWindow) ...@@ -448,4 +509,6 @@ BEGIN_EVENT_TABLE(DropDownList,wxPopupWindow)
EVT_LEFT_UP (DropDownList::onLeftUp) EVT_LEFT_UP (DropDownList::onLeftUp)
EVT_MOTION (DropDownList::onMotion) EVT_MOTION (DropDownList::onMotion)
EVT_LEAVE_WINDOW (DropDownList::onMouseLeave) EVT_LEAVE_WINDOW (DropDownList::onMouseLeave)
EVT_MOUSEWHEEL (DropDownList::onMouseWheel)
EVT_SCROLLWIN (DropDownList::onScroll)
END_EVENT_TABLE () END_EVENT_TABLE ()
...@@ -78,8 +78,8 @@ class DropDownList : public wxPopupWindow { ...@@ -78,8 +78,8 @@ class DropDownList : public wxPopupWindow {
// --------------------------------------------------- : Layout // --------------------------------------------------- : Layout
static const int marginW = 1; static const int marginW = 0;
static const int marginH = 1; static const int marginH = 0;
// may be changed by derived class // may be changed by derived class
int text_offset; ///< Vertical distance between top of item and text int text_offset; ///< Vertical distance between top of item and text
...@@ -89,14 +89,15 @@ class DropDownList : public wxPopupWindow { ...@@ -89,14 +89,15 @@ class DropDownList : public wxPopupWindow {
private: private:
// --------------------------------------------------- : Data // --------------------------------------------------- : Data
size_t selected_item; ///< The item that is selected, or NO_SELECTION size_t selected_item; ///< The item that is selected, or NO_SELECTION
bool mouse_down; ///< Is the mouse pressed? bool mouse_down; ///< Is the mouse pressed?
DropDownList* open_sub_menu; ///< The sub menu that is currently shown, if any DropDownList* open_sub_menu; ///< The sub menu that is currently shown, if any
DropDownList* parent_menu; ///< The parent menu, only applies to sub menus DropDownList* parent_menu; ///< The parent menu, only applies to sub menus
ValueViewer* viewer; ///< The parent viewer object (optional) ValueViewer* viewer; ///< The parent viewer object (optional)
DropDownHider* hider, *hider2; ///< Class to hide this window when we lose focus DropDownHider* hider, *hider2; ///< Class to hide this window when we lose focus
bool close_on_mouse_out; ///< Was the list kept open after selecting a choice, if so, be eager to close it bool close_on_mouse_out; ///< Was the list kept open after selecting a choice, if so, be eager to close it
int visible_start; ///< First visible pixel
// --------------------------------------------------- : Events // --------------------------------------------------- : Events
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
...@@ -105,16 +106,21 @@ class DropDownList : public wxPopupWindow { ...@@ -105,16 +106,21 @@ class DropDownList : public wxPopupWindow {
void onLeftUp (wxMouseEvent&); void onLeftUp (wxMouseEvent&);
void onMotion(wxMouseEvent&); void onMotion(wxMouseEvent&);
void onMouseLeave(wxMouseEvent&); void onMouseLeave(wxMouseEvent&);
void onMouseWheel(wxMouseEvent& ev);
void onScroll(wxScrollWinEvent&);
// --------------------------------------------------- : Privates // --------------------------------------------------- : Privates
/// Return the y coordinate of an item /// Return the y coordinate of an item (in scrolled coordinates)
int itemPosition(size_t item) const; int itemPosition(size_t item) const;
void realHide(); void realHide();
void hideSubMenu(); void hideSubMenu();
bool showSubMenu(); bool showSubMenu();
bool showSubMenu(size_t item, int y); bool showSubMenu(size_t item, int y);
bool selectItem(size_t item);
void ensureSelectedItemVisible();
void scrollTo(int pos);
void draw(DC& dc); void draw(DC& dc);
void drawItem(DC& dc, int y, size_t item); void drawItem(DC& dc, int y, size_t item);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment