small refactoring of FbTk/Menu code

* removed useless struct _menu
* code deduplication via renderMenuPixmap()
* improved documentation:
   - 'sublevel' actually means 'column'
   - 'persub' means 'row_per_column'
This commit is contained in:
Mathias Gumz 2011-01-15 21:31:37 +01:00
parent accb8c3fa3
commit bb480030f5
4 changed files with 254 additions and 229 deletions

View file

@ -75,6 +75,26 @@ using std::cout;
using std::endl; using std::endl;
#endif // DEBUG #endif // DEBUG
namespace {
// if 'win' is given, 'pm' is used as the backGroundPixmap
void renderMenuPixmap(Pixmap& pm, FbTk::FbWindow* win, int width, int height, const FbTk::Texture& tex, FbTk::ImageControl& img_ctrl) {
img_ctrl.removeImage(pm);
if (!tex.usePixmap()) {
pm = None;
if (win)
win->setBackgroundColor(tex.color());
} else {
pm = img_ctrl.renderImage(width, height, tex);
if (win)
win->setBackgroundPixmap(pm);
}
}
} // end of anonymous namespace
namespace FbTk { namespace FbTk {
Menu *Menu::shown = 0; Menu *Menu::shown = 0;
@ -118,84 +138,85 @@ Menu::Menu(FbTk::ThemeProxy<MenuTheme> &tm, ImageControl &imgctrl):
m_type_ahead.init(menuitems); m_type_ahead.init(menuitems);
menu.x_move = m_x_move =
menu.y_move = 0; m_y_move = 0;
m_which_sub = -1; m_which_sub = -1;
menu.frame_pixmap = m_frame_pixmap =
menu.title_pixmap = m_title_pixmap =
menu.hilite_pixmap = None; m_hilite_pixmap = None;
menu.item_w = menu.frame_h = m_item_w = m_frame_h =
theme()->titleFont().height() + theme()->bevelWidth() * 2; theme()->titleFont().height() + theme()->bevelWidth() * 2;
menu.sublevels = m_columns =
menu.persub = m_rows_per_column =
menu.minsub = 0; m_min_columns = 0;
FbTk::EventManager &evm = *FbTk::EventManager::instance();
long event_mask = ButtonPressMask | ButtonReleaseMask | long event_mask = ButtonPressMask | ButtonReleaseMask |
ButtonMotionMask | KeyPressMask | ExposureMask | FocusChangeMask; ButtonMotionMask | KeyPressMask | ExposureMask | FocusChangeMask;
// create menu window // create menu window
menu.window = FbTk::FbWindow(tm->screenNum(), m_window = FbTk::FbWindow(tm->screenNum(),
0, 0, 10, 10, 0, 0, 10, 10,
event_mask, event_mask,
true, // override redirect true, // override redirect
true); // save_under true); // save_under
// initialize 'shape' here AFTER we created menu.window aka fbwindow() // initialize 'shape' here AFTER we created m_window aka fbwindow()
m_shape.reset(new Shape(fbwindow(), tm->shapePlaces())); m_shape.reset(new Shape(fbwindow(), tm->shapePlaces()));
evm.add(*this, m_window);
// strip focus change mask from attrib, since we should only use it with main window // strip focus change mask from attrib, since we should only use it with main window
event_mask ^= FocusChangeMask; event_mask ^= FocusChangeMask;
FbTk::EventManager &evm = *FbTk::EventManager::instance();
evm.add(*this, menu.window);
event_mask |= EnterWindowMask | LeaveWindowMask; event_mask |= EnterWindowMask | LeaveWindowMask;
//create menu title //create menu title
menu.title = FbTk::FbWindow(menu.window, // parent m_title = FbTk::FbWindow(m_window, // parent
0, 0, width(), theme()->titleHeight(), // pos and size 0, 0, width(), theme()->titleHeight(), // pos and size
event_mask, // mask event_mask, // mask
false, // override redirect false, // override redirect
true); // save under true); // save under
evm.add(*this, menu.title); evm.add(*this, m_title);
menu.title.setRenderer(*this); m_title.setRenderer(*this);
event_mask |= PointerMotionMask; event_mask |= PointerMotionMask;
menu.frame = FbTk::FbWindow(menu.window, // parent m_frame = FbTk::FbWindow(m_window, // parent
0, theme()->titleHeight(), // pos 0, theme()->titleHeight(), // pos
width(), menu.frame_h ? menu.frame_h : 1, // size width(), m_frame_h ? m_frame_h : 1, // size
event_mask, // mask event_mask, // mask
false, // override redirect false, // override redirect
true); // save under true); // save under
evm.add(*this, menu.frame); evm.add(*this, m_frame);
menu.frame.setRenderer(*this); m_frame.setRenderer(*this);
menu.title.raise(); m_title.raise();
reconfigure(); reconfigure();
} }
Menu::~Menu() { Menu::~Menu() {
menu.window.hide(); m_window.hide();
if (shown && shown->window() == window()) if (shown && shown->window() == window())
shown = 0; shown = 0;
removeAll(); removeAll();
if (menu.title_pixmap) if (m_title_pixmap)
m_image_ctrl.removeImage(menu.title_pixmap); m_image_ctrl.removeImage(m_title_pixmap);
if (menu.frame_pixmap) if (m_frame_pixmap)
m_image_ctrl.removeImage(menu.frame_pixmap); m_image_ctrl.removeImage(m_frame_pixmap);
if (menu.hilite_pixmap) if (m_hilite_pixmap)
m_image_ctrl.removeImage(menu.hilite_pixmap); m_image_ctrl.removeImage(m_hilite_pixmap);
if (s_focused == this) if (s_focused == this)
s_focused = 0; s_focused = 0;
@ -290,11 +311,11 @@ void Menu::removeAll() {
} }
void Menu::raise() { void Menu::raise() {
menu.window.raise(); m_window.raise();
} }
void Menu::lower() { void Menu::lower() {
menu.window.lower(); m_window.lower();
} }
void Menu::cycleItems(bool reverse) { void Menu::cycleItems(bool reverse) {
@ -342,7 +363,7 @@ void Menu::setActiveIndex(int new_index) {
if (validIndex(old_active_index) && if (validIndex(old_active_index) &&
menuitems[old_active_index] != 0) { menuitems[old_active_index] != 0) {
if (menuitems[old_active_index]->submenu()) { if (menuitems[old_active_index]->submenu()) {
// we need to do this explicitly on the menu.window // we need to do this explicitly on the m_window
// since it might hide the parent if we use Menu::hide // since it might hide the parent if we use Menu::hide
menuitems[old_active_index]->submenu()->internal_hide(); menuitems[old_active_index]->submenu()->internal_hide();
} }
@ -378,10 +399,10 @@ void Menu::enableTitle() {
void Menu::updateMenu() { void Menu::updateMenu() {
if (m_title_vis) { if (m_title_vis) {
menu.item_w = theme()->titleFont().textWidth(menu.label); m_item_w = theme()->titleFont().textWidth(m_label);
menu.item_w += (theme()->bevelWidth() * 2); m_item_w += (theme()->bevelWidth() * 2);
} else } else
menu.item_w = 1; m_item_w = 1;
if (validIndex(m_active_index) && !menuitems[m_active_index]->isEnabled()) { if (validIndex(m_active_index) && !menuitems[m_active_index]->isEnabled()) {
// find the nearest enabled menuitem and highlight it // find the nearest enabled menuitem and highlight it
@ -403,68 +424,62 @@ void Menu::updateMenu() {
Menuitems::iterator it_end = menuitems.end(); Menuitems::iterator it_end = menuitems.end();
for (; it != it_end; ++it) { for (; it != it_end; ++it) {
ii = (*it)->width(theme()); ii = (*it)->width(theme());
menu.item_w = (ii > menu.item_w ? ii : menu.item_w); m_item_w = (ii > m_item_w ? ii : m_item_w);
} }
if (menu.item_w < 1) if (m_item_w < 1)
menu.item_w = 1; m_item_w = 1;
if (!menuitems.empty()) { if (!menuitems.empty()) {
menu.sublevels = 1; m_columns = 1;
while (theme()->itemHeight() * (menuitems.size() + 1) / menu.sublevels + while (theme()->itemHeight() * (menuitems.size() + 1) / m_columns +
theme()->titleHeight() + theme()->borderWidth() > m_screen_height) { theme()->titleHeight() + theme()->borderWidth() > m_screen_height) {
menu.sublevels++; m_columns++;
} }
if (menu.sublevels < menu.minsub) if (m_columns < m_min_columns)
menu.sublevels = menu.minsub; m_columns = m_min_columns;
menu.persub = menuitems.size() / menu.sublevels; m_rows_per_column = menuitems.size() / m_columns;
if (menuitems.size() % menu.sublevels) menu.persub++; if (menuitems.size() % m_columns) m_rows_per_column++;
} else { } else {
menu.sublevels = 0; m_columns = 0;
menu.persub = 0; m_rows_per_column = 0;
} }
int itmp = (theme()->itemHeight() * menu.persub); int itmp = (theme()->itemHeight() * m_rows_per_column);
menu.frame_h = itmp < 1 ? 1 : itmp; m_frame_h = itmp < 1 ? 1 : itmp;
unsigned int new_width = (menu.sublevels * menu.item_w); unsigned int new_width = (m_columns * m_item_w);
unsigned int new_height = menu.frame_h; unsigned int new_height = m_frame_h;
if (m_title_vis) if (m_title_vis)
new_height += theme()->titleHeight() + ((menu.frame_h > 0)?menu.title.borderWidth():0); new_height += theme()->titleHeight() + ((m_frame_h > 0)?m_title.borderWidth():0);
if (new_width == 0) if (new_width == 0)
new_width = menu.item_w; new_width = m_item_w;
if (new_height < 1) if (new_height < 1)
new_height = 1; new_height = 1;
// must update main window size whether visible or not // must update main window size whether visible or not
// the rest can wait until the end // the rest can wait until the end
if (menu.window.width() != new_width) if (m_window.width() != new_width)
m_need_update = true; m_need_update = true;
menu.window.resize(new_width, new_height); m_window.resize(new_width, new_height);
if (!isVisible()) if (!isVisible())
return; return;
if (menu.frame.alpha() != alpha()) if (m_frame.alpha() != alpha())
menu.frame.setAlpha(alpha()); m_frame.setAlpha(alpha());
Pixmap tmp = menu.hilite_pixmap; renderMenuPixmap(m_hilite_pixmap, NULL,
const FbTk::Texture &hilite_tex = theme()->hiliteTexture(); m_item_w, theme()->itemHeight(),
if (!hilite_tex.usePixmap()) { theme()->hiliteTexture(), m_image_ctrl);
menu.hilite_pixmap = None;
} else
menu.hilite_pixmap =
m_image_ctrl.renderImage(menu.item_w, theme()->itemHeight(), hilite_tex);
if (tmp)
m_image_ctrl.removeImage(tmp);
if (!theme()->selectedPixmap().pixmap().drawable()) { if (!theme()->selectedPixmap().pixmap().drawable()) {
@ -490,46 +505,24 @@ void Menu::updateMenu() {
} }
if (m_title_vis) { if (m_title_vis) {
menu.title.moveResize(-menu.title.borderWidth(), -menu.title.borderWidth(), m_title.moveResize(-m_title.borderWidth(), -m_title.borderWidth(),
width() + menu.title.borderWidth(), theme()->titleHeight()); width() + m_title.borderWidth(), theme()->titleHeight());
} }
menu.frame.moveResize(0, ((m_title_vis) ? menu.title.y() + menu.title.height() + m_frame.moveResize(0, ((m_title_vis) ? m_title.y() + m_title.height() +
menu.title.borderWidth()*2 : 0), m_title.borderWidth()*2 : 0),
width(), menu.frame_h); width(), m_frame_h);
if (m_title_vis && m_need_update) { if (m_title_vis && m_need_update) {
tmp = menu.title_pixmap; renderMenuPixmap(m_title_pixmap, &m_title,
const FbTk::Texture &tex = theme()->titleTexture(); width(), theme()->titleHeight(),
if (!tex.usePixmap()) { theme()->titleTexture(), m_image_ctrl);
menu.title_pixmap = None;
menu.title.setBackgroundColor(tex.color());
} else {
menu.title_pixmap =
m_image_ctrl.renderImage(width(), theme()->titleHeight(), tex);
// set pixmap that we have as real face to the user
menu.title.setBackgroundPixmap(menu.title_pixmap);
}
if (tmp)
m_image_ctrl.removeImage(tmp);
} }
tmp = menu.frame_pixmap;
const FbTk::Texture &frame_tex = theme()->frameTexture();
if (m_need_update) { if (m_need_update) {
if (!frame_tex.usePixmap()) { renderMenuPixmap(m_frame_pixmap, &m_frame,
menu.frame_pixmap = None; width(), m_frame_h,
menu.frame.setBackgroundColor(frame_tex.color()); theme()->frameTexture(), m_image_ctrl);
} else {
menu.frame_pixmap =
m_image_ctrl.renderImage(width(), menu.frame_h, frame_tex);
menu.frame.setBackgroundPixmap(menu.frame_pixmap);
}
if (tmp)
m_image_ctrl.removeImage(tmp);
} }
clearWindow(); clearWindow();
@ -551,8 +544,8 @@ void Menu::show() {
m_type_ahead.reset(); m_type_ahead.reset();
m_matches.clear(); m_matches.clear();
menu.window.showSubwindows(); m_window.showSubwindows();
menu.window.show(); m_window.show();
raise(); raise();
if (shown && shown != this) if (shown && shown != this)
@ -591,13 +584,13 @@ void Menu::grabInputFocus() {
s_focused = this; s_focused = this;
// grab input focus // grab input focus
menu.window.setInputFocus(RevertToPointerRoot, CurrentTime); m_window.setInputFocus(RevertToPointerRoot, CurrentTime);
} }
void Menu::clearWindow() { void Menu::clearWindow() {
menu.title.clear(); m_title.clear();
menu.frame.clear(); m_frame.clear();
// clear foreground bits of frame items // clear foreground bits of frame items
for (size_t i = 0; i < menuitems.size(); i++) { for (size_t i = 0; i < menuitems.size(); i++) {
@ -642,7 +635,7 @@ void Menu::internal_hide(bool first) {
m_parent->grabInputFocus(); m_parent->grabInputFocus();
m_parent = 0; m_parent = 0;
menu.window.hide(); m_window.hide();
} }
@ -650,10 +643,10 @@ void Menu::move(int x, int y) {
if (x == this->x() && y == this->y()) if (x == this->x() && y == this->y())
return; return;
menu.window.move(x, y); m_window.move(x, y);
// potentially transparent children // potentially transparent children
menu.title.parentMoved(); m_title.parentMoved();
menu.frame.parentMoved(); m_frame.parentMoved();
if (!isVisible()) if (!isVisible())
return; return;
@ -671,7 +664,7 @@ void Menu::redrawTitle(FbDrawable &drawable) {
const FbTk::Font &font = theme()->titleFont(); const FbTk::Font &font = theme()->titleFont();
int dx = theme()->bevelWidth(); int dx = theme()->bevelWidth();
unsigned int l = font.textWidth(menu.label) + theme()->bevelWidth()*2; unsigned int l = font.textWidth(m_label) + theme()->bevelWidth()*2;
switch (theme()->titleFontJustify()) { switch (theme()->titleFontJustify()) {
case FbTk::RIGHT: case FbTk::RIGHT:
@ -687,7 +680,7 @@ void Menu::redrawTitle(FbDrawable &drawable) {
// difference between height based on font, and style-set height // difference between height based on font, and style-set height
int height_offset = theme()->titleHeight() - (font.height() + 2*theme()->bevelWidth()); int height_offset = theme()->titleHeight() - (font.height() + 2*theme()->bevelWidth());
font.drawText(drawable, screenNumber(), theme()->titleTextGC().gc(), menu.label, font.drawText(drawable, screenNumber(), theme()->titleTextGC().gc(), m_label,
dx, font.ascent() + theme()->bevelWidth() + height_offset/2); // position dx, font.ascent() + theme()->bevelWidth() + height_offset/2); // position
} }
@ -713,19 +706,20 @@ void Menu::drawSubmenu(unsigned int index) {
item->submenu()->setScreen(m_screen_x, m_screen_y, m_screen_width, m_screen_height); item->submenu()->setScreen(m_screen_x, m_screen_y, m_screen_width, m_screen_height);
int sbl = index / menu.persub, i = index - (sbl * menu.persub); int column = index / m_rows_per_column;
int new_x = x() + ((menu.item_w * (sbl + 1)) + menu.window.borderWidth()); int row = index - (column * m_rows_per_column);
int new_x = x() + ((m_item_w * (column + 1)) + m_window.borderWidth());
int new_y; int new_y;
if (m_alignment == ALIGNTOP) { if (m_alignment == ALIGNTOP) {
new_y = (y() + ((m_title_vis) ? theme()->titleHeight() + menu.title.borderWidth() : 0) - new_y = (y() + ((m_title_vis) ? theme()->titleHeight() + m_title.borderWidth() : 0) -
((item->submenu()->m_title_vis) ? ((item->submenu()->m_title_vis) ?
item->submenu()->theme()->titleHeight() + menu.window.borderWidth() : 0)); item->submenu()->theme()->titleHeight() + m_window.borderWidth() : 0));
} else { } else {
new_y = (y() + (theme()->itemHeight() * i) + new_y = (y() + (theme()->itemHeight() * row) +
((m_title_vis) ? theme()->titleHeight() + menu.window.borderWidth() : 0) - ((m_title_vis) ? theme()->titleHeight() + m_window.borderWidth() : 0) -
((item->submenu()->m_title_vis) ? ((item->submenu()->m_title_vis) ?
item->submenu()->theme()->titleHeight() + menu.window.borderWidth() : 0)); item->submenu()->theme()->titleHeight() + m_window.borderWidth() : 0));
} }
if (m_alignment == ALIGNBOTTOM && if (m_alignment == ALIGNBOTTOM &&
@ -736,7 +730,7 @@ void Menu::drawSubmenu(unsigned int index) {
int borderw = item->submenu()->fbwindow().borderWidth(); int borderw = item->submenu()->fbwindow().borderWidth();
if ((new_x + item->submenu()->width()) + 2*borderw > m_screen_x + m_screen_width) { if ((new_x + item->submenu()->width()) + 2*borderw > m_screen_x + m_screen_width) {
new_x = x() - item->submenu()->width() - menu.window.borderWidth(); new_x = x() - item->submenu()->width() - m_window.borderWidth();
} }
if (new_x < m_screen_x) if (new_x < m_screen_x)
@ -744,7 +738,7 @@ void Menu::drawSubmenu(unsigned int index) {
if ((new_y + item->submenu()->height()) > m_screen_y + m_screen_height) { if ((new_y + item->submenu()->height()) > m_screen_y + m_screen_height) {
new_y = m_screen_y + m_screen_height - item->submenu()->height() - new_y = m_screen_y + m_screen_height - item->submenu()->height() -
menu.window.borderWidth() * 2; m_window.borderWidth() * 2;
} }
item->submenu()->m_moving = m_moving; item->submenu()->m_moving = m_moving;
@ -773,14 +767,16 @@ int Menu::drawItem(FbDrawable &drawable, unsigned int index,
bool highlight, bool exclusive_drawable) { bool highlight, bool exclusive_drawable) {
if (index >= menuitems.size() || menuitems.size() == 0 || if (index >= menuitems.size() || menuitems.size() == 0 ||
menu.persub == 0) m_rows_per_column == 0)
return 0; return 0;
MenuItem *item = menuitems[index]; MenuItem *item = menuitems[index];
if (! item) return 0; if (! item) return 0;
int sbl = index / menu.persub, i = index - (sbl * menu.persub); int column = index / m_rows_per_column;
int item_x = (sbl * menu.item_w), item_y = (i * theme()->itemHeight()); int row = index - (column * m_rows_per_column);
int item_x = (column * m_item_w);
int item_y = (row * theme()->itemHeight());
if (exclusive_drawable) if (exclusive_drawable)
item_x = item_y = 0; item_x = item_y = 0;
@ -788,14 +784,14 @@ int Menu::drawItem(FbDrawable &drawable, unsigned int index,
item->draw(drawable, theme(), highlight, item->draw(drawable, theme(), highlight,
exclusive_drawable, true, // draw fg, draw bg exclusive_drawable, true, // draw fg, draw bg
item_x, item_y, item_x, item_y,
menu.item_w, theme()->itemHeight()); m_item_w, theme()->itemHeight());
return item_y; return item_y;
} }
void Menu::setLabel(const FbTk::BiDiString &labelstr) { void Menu::setLabel(const FbTk::BiDiString &labelstr) {
//make sure we don't send 0 to std::string //make sure we don't send 0 to std::string
menu.label = labelstr; m_label = labelstr;
reconfigure(); reconfigure();
} }
@ -807,7 +803,6 @@ void Menu::setItemSelected(unsigned int index, bool sel) {
if (! item) return; if (! item) return;
item->setSelected(sel); item->setSelected(sel);
} }
@ -829,7 +824,6 @@ void Menu::setItemEnabled(unsigned int index, bool enable) {
if (! item) return; if (! item) return;
item->setEnabled(enable); item->setEnabled(enable);
} }
@ -860,7 +854,7 @@ void Menu::handleEvent(XEvent &event) {
s_focused = 0; s_focused = 0;
// I don't know why, but I get a FocusIn event when closing the menu with // I don't know why, but I get a FocusIn event when closing the menu with
// the mouse over it -- probably an xorg bug, but it's easy to address here // the mouse over it -- probably an xorg bug, but it's easy to address here
} else if (event.type == FocusIn && m_visible) { } else if (event.type == FocusIn && isVisible()) {
if (s_focused != this) if (s_focused != this)
s_focused = this; s_focused = this;
// if there's a submenu open, focus it instead // if there's a submenu open, focus it instead
@ -871,16 +865,17 @@ void Menu::handleEvent(XEvent &event) {
} }
void Menu::buttonPressEvent(XButtonEvent &be) { void Menu::buttonPressEvent(XButtonEvent &be) {
if (be.window == menu.title) { if (be.window == m_title) {
grabInputFocus(); grabInputFocus();
m_closing = (be.button == 3); m_closing = (be.button == 3);
} else } else
m_closing = false; m_closing = false;
if (be.window == menu.frame && menu.item_w != 0) { if (be.window == m_frame && m_item_w != 0) {
int sbl = (be.x / menu.item_w), i = (be.y / theme()->itemHeight()); int column = (be.x / m_item_w);
int w = (sbl * menu.persub) + i; int i = (be.y / theme()->itemHeight());
int w = (column * m_rows_per_column) + i;
if (validIndex(w) && isItemSelectable(static_cast<unsigned int>(w))) { if (validIndex(w) && isItemSelectable(static_cast<unsigned int>(w))) {
MenuItem *item = menuitems[w]; MenuItem *item = menuitems[w];
@ -891,14 +886,14 @@ void Menu::buttonPressEvent(XButtonEvent &be) {
} }
} }
} else { } else {
menu.x_move = be.x_root - x(); m_x_move = be.x_root - x();
menu.y_move = be.y_root - y(); m_y_move = be.y_root - y();
} }
} }
void Menu::buttonReleaseEvent(XButtonEvent &re) { void Menu::buttonReleaseEvent(XButtonEvent &re) {
if (re.window == menu.title) { if (re.window == m_title) {
if (m_moving) { if (m_moving) {
m_moving = false; m_moving = false;
@ -908,8 +903,8 @@ void Menu::buttonReleaseEvent(XButtonEvent &re) {
if (alpha() < 255) { if (alpha() < 255) {
// update these since we've (probably) moved // update these since we've (probably) moved
menu.title.parentMoved(); m_title.parentMoved();
menu.frame.parentMoved(); m_frame.parentMoved();
clearWindow(); clearWindow();
} }
} }
@ -917,15 +912,17 @@ void Menu::buttonReleaseEvent(XButtonEvent &re) {
if (re.button == 3 && m_closing) if (re.button == 3 && m_closing)
internal_hide(); internal_hide();
} else if (re.window == menu.frame) { } else if (re.window == m_frame) {
int sbl = (re.x / menu.item_w), i = (re.y / theme()->itemHeight()), int column = (re.x / m_item_w);
ix = sbl * menu.item_w, iy = i * theme()->itemHeight(), int i = (re.y / theme()->itemHeight());
w = (sbl * menu.persub) + i; int ix = column * m_item_w;
int iy = i * theme()->itemHeight();
int w = (column * m_rows_per_column) + i;
if (validIndex(w) && isItemSelectable(static_cast<unsigned int>(w))) { if (validIndex(w) && isItemSelectable(static_cast<unsigned int>(w))) {
if (m_active_index == w && isItemEnabled(w) && if (m_active_index == w && isItemEnabled(w) &&
re.x > ix && re.x < (signed) (ix + menu.item_w) && re.x > ix && re.x < (signed) (ix + m_item_w) &&
re.y > iy && re.y < (signed) (iy + theme()->itemHeight())) { re.y > iy && re.y < (signed) (iy + theme()->itemHeight())) {
menuitems[w]->click(re.button, re.time, re.state); menuitems[w]->click(re.button, re.time, re.state);
} else { } else {
@ -941,7 +938,7 @@ void Menu::buttonReleaseEvent(XButtonEvent &re) {
void Menu::motionNotifyEvent(XMotionEvent &me) { void Menu::motionNotifyEvent(XMotionEvent &me) {
// if draging the with the titlebar: // if draging the with the titlebar:
if (me.window == menu.title && (me.state & Button1Mask)) { if (me.window == m_title && (me.state & Button1Mask)) {
stopHide(); stopHide();
if (! m_moving) { if (! m_moving) {
@ -958,14 +955,14 @@ void Menu::motionNotifyEvent(XMotionEvent &me) {
} else { } else {
// we dont call ::move here 'cause we dont want to update transparency // we dont call ::move here 'cause we dont want to update transparency
// while draging the menu (which is slow) // while draging the menu (which is slow)
menu.window.move(me.x_root - menu.x_move, me.y_root - menu.y_move); m_window.move(me.x_root - m_x_move, me.y_root - m_y_move);
} }
} else if (!(me.state & Button1Mask) && me.window == menu.frame) { } else if (!(me.state & Button1Mask) && me.window == m_frame) {
stopHide(); stopHide();
int sbl = (me.x / menu.item_w), int column = (me.x / m_item_w);
i = (me.y / theme()->itemHeight()), int i = (me.y / theme()->itemHeight());
w = (sbl * menu.persub) + i; int w = (column * m_rows_per_column) + i;
if (w == m_active_index || !validIndex(w)) if (w == m_active_index || !validIndex(w))
return; return;
@ -994,12 +991,8 @@ void Menu::motionNotifyEvent(XMotionEvent &me) {
} }
if (itmp->submenu()) { if (itmp->submenu()) { // start submenu open delay
// start submenu open delay m_submenu_timer.setTimeout(0, theme()->getDelay() * 1000);
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = theme()->getDelay() * 1000; // transformed to usec
m_submenu_timer.setTimeout(timeout);
m_submenu_timer.start(); m_submenu_timer.start();
} else if (isItemSelectable(w)){ } else if (isItemSelectable(w)){
// else normal menu item // else normal menu item
@ -1012,32 +1005,37 @@ void Menu::motionNotifyEvent(XMotionEvent &me) {
void Menu::exposeEvent(XExposeEvent &ee) { void Menu::exposeEvent(XExposeEvent &ee) {
if (ee.window == menu.title) { if (ee.window == m_title) {
menu.title.clearArea(ee.x, ee.y, ee.width, ee.height); m_title.clearArea(ee.x, ee.y, ee.width, ee.height);
} else if (ee.window == menu.frame) { } else if (ee.window == m_frame) {
// find where to clear // find where to clear
// this is a compilicated algorithm... lets do it step by step... // this is a compilicated algorithm... lets do it step by step...
// first... we see in which sub level the expose starts... and how many // first... we see in which column the expose starts... and how many
// items down in that sublevel // items down in that column
int column = (ee.x / m_item_w);
int id = (ee.y / theme()->itemHeight());
int sbl = (ee.x / menu.item_w), id = (ee.y / theme()->itemHeight()), // next... figure out how many sublevels over the redrawspans
// next... figure out how many sublevels over the redrawspans int column_d = ((ee.x + ee.width) / m_item_w);
sbl_d = ((ee.x + ee.width) / menu.item_w),
// then we see how many items down to redraw
id_d = ((ee.y + ee.height) / theme()->itemHeight());
if (id_d > menu.persub) id_d = menu.persub; // then we see how many items down to redraw
// draw the sublevels and the number of items the exposure spans int id_d = ((ee.y + ee.height) / theme()->itemHeight());
if (id_d > m_rows_per_column)
id_d = m_rows_per_column;
// draw the columns and the number of items the exposure spans
int i, ii; int i, ii;
for (i = sbl; i <= sbl_d; i++) { for (i = column; i <= column_d; i++) {
// set the iterator to the first item in the sublevel needing redrawing // set the iterator to the first item in the column needing redrawing
int index = id + i * menu.persub; int index = id + i * m_rows_per_column;
if (index < static_cast<int>(menuitems.size()) && index >= 0) { if (index < static_cast<int>(menuitems.size()) && index >= 0) {
Menuitems::iterator it = menuitems.begin() + index; Menuitems::iterator it = menuitems.begin() + index;
Menuitems::iterator it_end = menuitems.end(); Menuitems::iterator it_end = menuitems.end();
for (ii = id; ii <= id_d && it != it_end; ++it, ii++) { for (ii = id; ii <= id_d && it != it_end; ++it, ii++) {
int index = ii + (i * menu.persub); int index = ii + (i * m_rows_per_column);
// redraw the item // redraw the item
clearItem(index); clearItem(index);
} }
@ -1065,10 +1063,10 @@ void Menu::keyPressEvent(XKeyEvent &event) {
break; break;
case XK_Left: // enter parent if we have one case XK_Left: // enter parent if we have one
resetTypeAhead(); resetTypeAhead();
if (menu.sublevels > 1 && m_active_index >= menu.persub) { if (m_columns > 1 && m_active_index >= m_rows_per_column) {
int new_index = m_active_index - menu.persub; int new_index = m_active_index - m_rows_per_column;
while (new_index >= 0 && !isItemEnabled(new_index)) while (new_index >= 0 && !isItemEnabled(new_index))
new_index -= menu.persub; new_index -= m_rows_per_column;
if (new_index >= 0) if (new_index >= 0)
setActiveIndex(new_index); setActiveIndex(new_index);
} else } else
@ -1076,11 +1074,11 @@ void Menu::keyPressEvent(XKeyEvent &event) {
break; break;
case XK_Right: // enter submenu if we have one case XK_Right: // enter submenu if we have one
resetTypeAhead(); resetTypeAhead();
if (menu.sublevels > 1 && validIndex(m_active_index) && if (m_columns > 1 && validIndex(m_active_index) &&
validIndex(m_active_index + menu.persub)) { validIndex(m_active_index + m_rows_per_column)) {
int new_index = m_active_index + menu.persub; int new_index = m_active_index + m_rows_per_column;
while (validIndex(new_index) && !isItemEnabled(new_index)) while (validIndex(new_index) && !isItemEnabled(new_index))
new_index += menu.persub; new_index += m_rows_per_column;
if (validIndex(new_index)) if (validIndex(new_index))
setActiveIndex(new_index); setActiveIndex(new_index);
} else } else
@ -1094,8 +1092,8 @@ void Menu::keyPressEvent(XKeyEvent &event) {
case XK_BackSpace: case XK_BackSpace:
if (m_type_ahead.stringSize() == 0) { if (m_type_ahead.stringSize() == 0) {
internal_hide(); internal_hide();
break; break;
} }
m_type_ahead.putBackSpace(); m_type_ahead.putBackSpace();
drawTypeAheadItems(); drawTypeAheadItems();
@ -1157,23 +1155,23 @@ void Menu::reconfigure() {
m_shape->setPlaces(theme()->shapePlaces()); m_shape->setPlaces(theme()->shapePlaces());
if (FbTk::Transparent::haveComposite()) { if (FbTk::Transparent::haveComposite()) {
menu.window.setOpaque(alpha()); m_window.setOpaque(alpha());
menu.title.setAlpha(255); m_title.setAlpha(255);
menu.frame.setAlpha(255); m_frame.setAlpha(255);
} else { } else {
menu.window.setOpaque(255); m_window.setOpaque(255);
menu.title.setAlpha(alpha()); m_title.setAlpha(alpha());
menu.frame.setAlpha(alpha()); m_frame.setAlpha(alpha());
} }
m_need_update = true; // redraw items m_need_update = true; // redraw items
menu.window.setBorderColor(theme()->borderColor()); m_window.setBorderColor(theme()->borderColor());
menu.title.setBorderColor(theme()->borderColor()); m_title.setBorderColor(theme()->borderColor());
menu.frame.setBorderColor(theme()->borderColor()); m_frame.setBorderColor(theme()->borderColor());
menu.window.setBorderWidth(theme()->borderWidth()); m_window.setBorderWidth(theme()->borderWidth());
menu.title.setBorderWidth(theme()->borderWidth()); m_title.setBorderWidth(theme()->borderWidth());
updateMenu(); updateMenu();
} }
@ -1237,9 +1235,9 @@ void Menu::setScreen(int x, int y, int w, int h) {
// Render the foreground objects of given window onto given pixmap // Render the foreground objects of given window onto given pixmap
void Menu::renderForeground(FbWindow &win, FbDrawable &drawable) { void Menu::renderForeground(FbWindow &win, FbDrawable &drawable) {
if (&win == &menu.frame) { if (&win == &m_frame) {
redrawFrame(drawable); redrawFrame(drawable);
} else if (&win == &menu.title) { } else if (&win == &m_title) {
redrawTitle(drawable); redrawTitle(drawable);
} }
} }
@ -1252,9 +1250,12 @@ void Menu::clearItem(int index, bool clear, int search_index) {
if (!validIndex(index)) if (!validIndex(index))
return; return;
int sbl = index / menu.persub, i = index - (sbl * menu.persub); int column = index / m_rows_per_column;
unsigned int item_w = menu.item_w, item_h = theme()->itemHeight(); int row = index - (column * m_rows_per_column);
int item_x = (sbl * item_w), item_y = (i * item_h); unsigned int item_w = m_item_w;
unsigned int item_h = theme()->itemHeight();
int item_x = (column * item_w);
int item_y = (row * item_h);
bool highlight = (index == m_active_index && isItemSelectable(index)); bool highlight = (index == m_active_index && isItemSelectable(index));
if (search_index < 0) if (search_index < 0)
@ -1269,12 +1270,12 @@ void Menu::clearItem(int index, bool clear, int search_index) {
drawLine(index, m_type_ahead.stringSize()); drawLine(index, m_type_ahead.stringSize());
return; return;
} else if (clear) } else if (clear)
menu.frame.clearArea(item_x, item_y, item_w, item_h); m_frame.clearArea(item_x, item_y, item_w, item_h);
MenuItem *item = menuitems[index]; MenuItem *item = menuitems[index];
if (! item) return; if (! item) return;
item->draw(menu.frame, theme(), highlight, item->draw(m_frame, theme(), highlight,
true, false, item_x, item_y, true, false, item_x, item_y,
item_w, item_h); item_w, item_h);
@ -1284,14 +1285,17 @@ void Menu::clearItem(int index, bool clear, int search_index) {
// Area must have been cleared before calling highlight // Area must have been cleared before calling highlight
void Menu::highlightItem(int index) { void Menu::highlightItem(int index) {
int sbl = index / menu.persub, i = index - (sbl * menu.persub); int column = index / m_rows_per_column;
unsigned int item_w = menu.item_w, item_h = theme()->itemHeight(); int row = index - (column * m_rows_per_column);
int item_x = (sbl * menu.item_w), item_y = (i * item_h); unsigned int item_w = m_item_w;
unsigned int item_h = theme()->itemHeight();
int item_x = (column * m_item_w);
int item_y = (row * item_h);
FbPixmap buffer = FbPixmap(menu.frame, item_w, item_h, menu.frame.depth()); FbPixmap buffer = FbPixmap(m_frame, item_w, item_h, m_frame.depth());
bool parent_rel = menu.hilite_pixmap == ParentRelative; bool parent_rel = m_hilite_pixmap == ParentRelative;
Pixmap pixmap = parent_rel ? menu.frame_pixmap : menu.hilite_pixmap; Pixmap pixmap = parent_rel ? m_frame_pixmap : m_hilite_pixmap;
int pixmap_x = parent_rel ? item_x : 0, pixmap_y = parent_rel ? item_y : 0; int pixmap_x = parent_rel ? item_x : 0, pixmap_y = parent_rel ? item_y : 0;
if (pixmap) { if (pixmap) {
buffer.copyArea(pixmap, buffer.copyArea(pixmap,
@ -1302,11 +1306,11 @@ void Menu::highlightItem(int index) {
buffer.fillRectangle(theme()->hiliteGC().gc(), buffer.fillRectangle(theme()->hiliteGC().gc(),
0, 0, item_w, item_h); 0, 0, item_w, item_h);
} }
menu.frame.updateTransparent(item_x, item_y, item_w, item_h, buffer.drawable(), true); m_frame.updateTransparent(item_x, item_y, item_w, item_h, buffer.drawable(), true);
drawItem(buffer, index, true, true); drawItem(buffer, index, true, true);
menu.frame.copyArea(buffer.drawable(), theme()->hiliteGC().gc(), m_frame.copyArea(buffer.drawable(), theme()->hiliteGC().gc(),
0, 0, 0, 0,
item_x, item_y, item_x, item_y,
item_w, item_h); item_w, item_h);
@ -1338,11 +1342,13 @@ void Menu::drawLine(int index, int size){
if (!validIndex(index)) if (!validIndex(index))
return; return;
int sbl = index / menu.persub, i = index - (sbl * menu.persub); int column = index / m_rows_per_column;
int item_x = (sbl * menu.item_w), item_y = (i * theme()->itemHeight()); int row = index - (column * m_rows_per_column);
int item_x = (column * m_item_w);
int item_y = (row * theme()->itemHeight());
FbTk::MenuItem *item = find(index); FbTk::MenuItem *item = find(index);
item->drawLine(menu.frame, theme(), size, item_x, item_y, menu.item_w); item->drawLine(m_frame, theme(), size, item_x, item_y, m_item_w);
} }
void Menu::hideShownMenu() { void Menu::hideShownMenu() {

View file

@ -114,7 +114,7 @@ public:
virtual void updateMenu(); virtual void updateMenu();
void setItemSelected(unsigned int index, bool val); void setItemSelected(unsigned int index, bool val);
void setItemEnabled(unsigned int index, bool val); void setItemEnabled(unsigned int index, bool val);
void setMinimumSublevels(int m) { menu.minsub = m; } void setMinimumColumns(int columns) { m_min_columns = columns; }
virtual void drawSubmenu(unsigned int index); virtual void drawSubmenu(unsigned int index);
/// show menu /// show menu
virtual void show(); virtual void show();
@ -130,17 +130,17 @@ public:
bool isTorn() const { return m_torn; } bool isTorn() const { return m_torn; }
bool isVisible() const { return m_visible; } bool isVisible() const { return m_visible; }
bool isMoving() const { return m_moving; } bool isMoving() const { return m_moving; }
int screenNumber() const { return menu.window.screenNumber(); } int screenNumber() const { return m_window.screenNumber(); }
Window window() const { return menu.window.window(); } Window window() const { return m_window.window(); }
FbWindow &fbwindow() { return menu.window; } FbWindow &fbwindow() { return m_window; }
const FbWindow &fbwindow() const { return menu.window; } const FbWindow &fbwindow() const { return m_window; }
FbWindow &titleWindow() { return menu.title; } FbWindow &titleWindow() { return m_title; }
FbWindow &frameWindow() { return menu.frame; } FbWindow &frameWindow() { return m_frame; }
const FbTk::BiDiString &label() const { return menu.label; } const FbTk::BiDiString &label() const { return m_label; }
int x() const { return menu.window.x(); } int x() const { return m_window.x(); }
int y() const { return menu.window.y(); } int y() const { return m_window.y(); }
unsigned int width() const { return menu.window.width(); } unsigned int width() const { return m_window.width(); }
unsigned int height() const { return menu.window.height(); } unsigned int height() const { return m_window.height(); }
size_t numberOfItems() const { return menuitems.size(); } size_t numberOfItems() const { return menuitems.size(); }
int currentSubmenu() const { return m_which_sub; } int currentSubmenu() const { return m_which_sub; }
@ -221,18 +221,37 @@ private:
int m_which_sub; int m_which_sub;
Alignment m_alignment; Alignment m_alignment;
struct _menu { // the menu window
Pixmap frame_pixmap, title_pixmap, hilite_pixmap; FbTk::FbWindow m_window;
FbTk::FbWindow window, frame, title; Pixmap m_hilite_pixmap;
FbTk::BiDiString label; // the title
int x_move, y_move, sublevels, persub, minsub, grab_x, grab_y; FbTk::FbWindow m_title;
Pixmap m_title_pixmap;
FbTk::BiDiString m_label;
unsigned int frame_h, item_w; // area for the menuitems
} menu; FbTk::FbWindow m_frame;
Pixmap m_frame_pixmap;
unsigned int m_frame_h;
int m_x_move;
int m_y_move;
int m_grab_x;
int m_grab_y;
// the menuitems are rendered in a grid with
// 'm_columns' (a minimum of 'm_min_columns') and
// a max of 'm_rows_per_column'
int m_columns;
int m_rows_per_column;
int m_min_columns;
unsigned int m_item_w;
int m_active_index; ///< current highlighted index int m_active_index; ///< current highlighted index
// the corners
std::auto_ptr<FbTk::Shape> m_shape; std::auto_ptr<FbTk::Shape> m_shape;
Drawable m_root_pm; Drawable m_root_pm;

View file

@ -1647,7 +1647,7 @@ void BScreen::setupConfigmenu(FbTk::Menu &menu) {
{ _FB_XTEXT(Align, BottomRight, "Bottom Right", "Bottom Right"), FbWinFrame::BOTTOMRIGHT} { _FB_XTEXT(Align, BottomRight, "Bottom Right", "Bottom Right"), FbWinFrame::BOTTOMRIGHT}
}; };
tabplacement_menu->setMinimumSublevels(3); tabplacement_menu->setMinimumColumns(3);
// create items in sub menu // create items in sub menu
for (size_t i=0; i< sizeof(place_menu)/sizeof(PlacementP); ++i) { for (size_t i=0; i< sizeof(place_menu)/sizeof(PlacementP); ++i) {
const PlacementP& p = place_menu[i]; const PlacementP& p = place_menu[i];

View file

@ -1241,7 +1241,7 @@ void Slit::setupMenu() {
// setup sub menu // setup sub menu
placement_menu->setLabel(_FB_XTEXT(Slit, Placement, "Slit Placement", "Slit Placement")); placement_menu->setLabel(_FB_XTEXT(Slit, Placement, "Slit Placement", "Slit Placement"));
placement_menu->setMinimumSublevels(3); placement_menu->setMinimumColumns(3);
m_layermenu->setInternalMenu(); m_layermenu->setInternalMenu();
m_clientlist_menu.setInternalMenu(); m_clientlist_menu.setInternalMenu();
m_slitmenu.setInternalMenu(); m_slitmenu.setInternalMenu();