diff options
author | fluxgen <fluxgen> | 2003-07-02 05:26:45 (GMT) |
---|---|---|
committer | fluxgen <fluxgen> | 2003-07-02 05:26:45 (GMT) |
commit | 10d70ecd54fc678499786ab84279223c5bd776e3 (patch) | |
tree | 8b51bbca3c363c022db96e7ef0dd31fc2913a66e | |
parent | 2737e94b242ffc38f4d6760512ba794755603bf7 (diff) | |
download | fluxbox_lack-10d70ecd54fc678499786ab84279223c5bd776e3.zip fluxbox_lack-10d70ecd54fc678499786ab84279223c5bd776e3.tar.bz2 |
added keyboard navigation
-rw-r--r-- | src/FbTk/Menu.cc | 160 | ||||
-rw-r--r-- | src/FbTk/Menu.hh | 14 |
2 files changed, 168 insertions, 6 deletions
diff --git a/src/FbTk/Menu.cc b/src/FbTk/Menu.cc index 04bcc03..c953be9 100644 --- a/src/FbTk/Menu.cc +++ b/src/FbTk/Menu.cc | |||
@@ -22,7 +22,7 @@ | |||
22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
23 | // DEALINGS IN THE SOFTWARE. | 23 | // DEALINGS IN THE SOFTWARE. |
24 | 24 | ||
25 | // $Id: Menu.cc,v 1.24 2003/05/24 12:34:16 rathnor Exp $ | 25 | // $Id: Menu.cc,v 1.25 2003/07/02 05:26:14 fluxgen Exp $ |
26 | 26 | ||
27 | //use GNU extensions | 27 | //use GNU extensions |
28 | #ifndef _GNU_SOURCE | 28 | #ifndef _GNU_SOURCE |
@@ -39,6 +39,8 @@ | |||
39 | #include "Transparent.hh" | 39 | #include "Transparent.hh" |
40 | 40 | ||
41 | #include <X11/Xatom.h> | 41 | #include <X11/Xatom.h> |
42 | #include <X11/keysym.h> | ||
43 | |||
42 | #include <cstdio> | 44 | #include <cstdio> |
43 | #include <cstdlib> | 45 | #include <cstdlib> |
44 | #include <cstring> | 46 | #include <cstring> |
@@ -76,10 +78,12 @@ namespace FbTk { | |||
76 | static Menu *shown = 0; | 78 | static Menu *shown = 0; |
77 | 79 | ||
78 | unsigned char Menu::s_alpha = 255; | 80 | unsigned char Menu::s_alpha = 255; |
81 | Menu *Menu::s_focused = 0; | ||
79 | 82 | ||
80 | Menu::Menu(MenuTheme &tm, int screen_num, ImageControl &imgctrl): | 83 | Menu::Menu(MenuTheme &tm, int screen_num, ImageControl &imgctrl): |
81 | m_theme(tm), | 84 | m_theme(tm), |
82 | m_screen_num(screen_num), | 85 | m_screen_num(screen_num), |
86 | m_prev_focused_window(0), | ||
83 | m_image_ctrl(imgctrl), | 87 | m_image_ctrl(imgctrl), |
84 | m_display(FbTk::App::instance()->display()), | 88 | m_display(FbTk::App::instance()->display()), |
85 | m_parent(0), | 89 | m_parent(0), |
@@ -143,13 +147,15 @@ Menu::Menu(MenuTheme &tm, int screen_num, ImageControl &imgctrl): | |||
143 | XSetWindowAttributes attrib; | 147 | XSetWindowAttributes attrib; |
144 | attrib.override_redirect = True; | 148 | attrib.override_redirect = True; |
145 | attrib.event_mask = ButtonPressMask | ButtonReleaseMask | | 149 | attrib.event_mask = ButtonPressMask | ButtonReleaseMask | |
146 | ButtonMotionMask | KeyPressMask | ExposureMask; | 150 | ButtonMotionMask | KeyPressMask | ExposureMask | FocusChangeMask; |
147 | 151 | ||
148 | //create menu window | 152 | //create menu window |
149 | menu.window = XCreateWindow(m_display, RootWindow(m_display, screen_num), | 153 | menu.window = XCreateWindow(m_display, RootWindow(m_display, screen_num), |
150 | menu.x, menu.y, menu.width, menu.height, | 154 | menu.x, menu.y, menu.width, menu.height, |
151 | 0, CopyFromParent, | 155 | 0, CopyFromParent, |
152 | InputOutput, CopyFromParent, attrib_mask, &attrib); | 156 | InputOutput, CopyFromParent, attrib_mask, &attrib); |
157 | // strip focus change mask from attrib, since we should only use it with main window | ||
158 | attrib.event_mask ^= FocusChangeMask; | ||
153 | 159 | ||
154 | FbTk::EventManager &evm = *FbTk::EventManager::instance(); | 160 | FbTk::EventManager &evm = *FbTk::EventManager::instance(); |
155 | evm.add(*this, menu.window); | 161 | evm.add(*this, menu.window); |
@@ -199,7 +205,8 @@ Menu::~Menu() { | |||
199 | evm.remove(menu.title); | 205 | evm.remove(menu.title); |
200 | evm.remove(menu.frame); | 206 | evm.remove(menu.frame); |
201 | evm.remove(menu.window); | 207 | evm.remove(menu.window); |
202 | 208 | if (s_focused == this) | |
209 | s_focused = 0; | ||
203 | } | 210 | } |
204 | 211 | ||
205 | int Menu::insert(const char *label, RefCount<Command> &cmd, int pos) { | 212 | int Menu::insert(const char *label, RefCount<Command> &cmd, int pos) { |
@@ -280,6 +287,80 @@ void Menu::lower() { | |||
280 | menu.window.lower(); | 287 | menu.window.lower(); |
281 | } | 288 | } |
282 | 289 | ||
290 | void Menu::nextItem() { | ||
291 | if (which_press == menuitems.size() - 1) | ||
292 | return; | ||
293 | |||
294 | int old_which_press = which_press; | ||
295 | |||
296 | if (old_which_press >= 0 && | ||
297 | old_which_press < menuitems.size() && | ||
298 | menuitems[old_which_press] != 0) { | ||
299 | if (menuitems[old_which_press]->submenu()) { | ||
300 | // we need to do this explicitly on the menu.window | ||
301 | // since it might hide the parent if we use Menu::hide | ||
302 | menuitems[old_which_press]->submenu()->menu.window.hide(); | ||
303 | } | ||
304 | drawItem(old_which_press, false, true, true); | ||
305 | } | ||
306 | |||
307 | // restore old in case we changed which_press | ||
308 | which_press = old_which_press; | ||
309 | if (which_press < 0 || which_press >= menuitems.size()) | ||
310 | which_press = 0; | ||
311 | else if (which_press < menuitems.size() - 1) | ||
312 | which_press++; | ||
313 | |||
314 | |||
315 | if (menuitems[which_press] == 0) | ||
316 | return; | ||
317 | |||
318 | if (menuitems[which_press]->submenu()) | ||
319 | drawSubmenu(which_press); | ||
320 | else | ||
321 | drawItem(which_press, true, true, true); | ||
322 | |||
323 | #ifdef DEBUG | ||
324 | cerr<<__FILE__<<"("<<__FUNCTION__<<")"<<endl; | ||
325 | cerr<<"which_press = "<<which_press<<endl; | ||
326 | #endif // DEBUG | ||
327 | |||
328 | } | ||
329 | |||
330 | void Menu::prevItem() { | ||
331 | |||
332 | int old_which_press = which_press; | ||
333 | |||
334 | if (old_which_press >= 0 && old_which_press < menuitems.size()) { | ||
335 | if (menuitems[old_which_press]->submenu()) { | ||
336 | // we need to do this explicitly on the menu.window | ||
337 | // since it might hide the parent if we use Menu::hide | ||
338 | menuitems[old_which_press]->submenu()->menu.window.hide(); | ||
339 | } | ||
340 | drawItem(old_which_press, false, true, true); | ||
341 | } | ||
342 | // restore old in case we changed which_press | ||
343 | which_press = old_which_press; | ||
344 | |||
345 | if (which_press < 0 || which_press >= menuitems.size()) | ||
346 | which_press = 0; | ||
347 | else if (which_press - 1 >= 0) | ||
348 | which_press--; | ||
349 | |||
350 | if (menuitems[which_press] != 0) { | ||
351 | if (menuitems[which_press]->submenu()) | ||
352 | drawSubmenu(which_press); | ||
353 | else | ||
354 | drawItem(which_press, true, true, true); | ||
355 | } | ||
356 | |||
357 | #ifdef DEBUG | ||
358 | cerr<<__FILE__<<"("<<__FUNCTION__<<")"<<endl; | ||
359 | cerr<<"which_press = "<<which_press<<endl; | ||
360 | #endif // DEBUG | ||
361 | |||
362 | } | ||
363 | |||
283 | void Menu::disableTitle() { | 364 | void Menu::disableTitle() { |
284 | setTitleVisibility(false); | 365 | setTitleVisibility(false); |
285 | } | 366 | } |
@@ -486,8 +567,18 @@ void Menu::hide() { | |||
486 | p->internal_hide(); | 567 | p->internal_hide(); |
487 | } else | 568 | } else |
488 | internal_hide(); | 569 | internal_hide(); |
570 | |||
571 | } | ||
572 | |||
573 | void Menu::grabInputFocus() { | ||
574 | s_focused = this; | ||
575 | |||
576 | // grab input focus | ||
577 | menu.window.setInputFocus(RevertToPointerRoot, CurrentTime); | ||
578 | |||
489 | } | 579 | } |
490 | 580 | ||
581 | |||
491 | void Menu::clearWindow() { | 582 | void Menu::clearWindow() { |
492 | menu.window.clear(); | 583 | menu.window.clear(); |
493 | menu.title.clear(); | 584 | menu.title.clear(); |
@@ -925,9 +1016,22 @@ bool Menu::isItemEnabled(unsigned int index) const { | |||
925 | return item->isEnabled(); | 1016 | return item->isEnabled(); |
926 | } | 1017 | } |
927 | 1018 | ||
1019 | void Menu::handleEvent(XEvent &event) { | ||
1020 | if (event.type == FocusOut) { | ||
1021 | cerr<<"Focus out"<<endl; | ||
1022 | if (s_focused == this) | ||
1023 | s_focused = 0; | ||
1024 | } else if (event.type == FocusIn) { | ||
1025 | cerr<<"Focus in"<<endl; | ||
1026 | if (s_focused != this) | ||
1027 | s_focused = this; | ||
1028 | } | ||
1029 | } | ||
928 | 1030 | ||
929 | void Menu::buttonPressEvent(XButtonEvent &be) { | 1031 | void Menu::buttonPressEvent(XButtonEvent &be) { |
1032 | grabInputFocus(); | ||
930 | if (be.window == menu.frame && menu.item_h != 0 && menu.item_w != 0) { | 1033 | if (be.window == menu.frame && menu.item_h != 0 && menu.item_w != 0) { |
1034 | |||
931 | int sbl = (be.x / menu.item_w), i = (be.y / menu.item_h); | 1035 | int sbl = (be.x / menu.item_w), i = (be.y / menu.item_h); |
932 | int w = (sbl * menu.persub) + i; | 1036 | int w = (sbl * menu.persub) + i; |
933 | 1037 | ||
@@ -1025,7 +1129,7 @@ void Menu::motionNotifyEvent(XMotionEvent &me) { | |||
1025 | if ((i != which_press || sbl != which_sbl) && | 1129 | if ((i != which_press || sbl != which_sbl) && |
1026 | (w < static_cast<int>(menuitems.size()) && w >= 0)) { | 1130 | (w < static_cast<int>(menuitems.size()) && w >= 0)) { |
1027 | if (which_press != -1 && which_sbl != -1) { | 1131 | if (which_press != -1 && which_sbl != -1) { |
1028 | int p = (which_sbl * menu.persub) + which_press; | 1132 | int p = which_sbl * menu.persub + which_press; |
1029 | MenuItem *item = menuitems[p]; | 1133 | MenuItem *item = menuitems[p]; |
1030 | 1134 | ||
1031 | drawItem(p, false, true, true); | 1135 | drawItem(p, false, true, true); |
@@ -1155,6 +1259,54 @@ void Menu::leaveNotifyEvent(XCrossingEvent &ce) { | |||
1155 | } | 1259 | } |
1156 | } | 1260 | } |
1157 | 1261 | ||
1262 | void Menu::keyPressEvent(XKeyEvent &event) { | ||
1263 | KeySym ks; | ||
1264 | char keychar[1]; | ||
1265 | XLookupString(&event, keychar, 1, &ks, 0); | ||
1266 | // a modifier key by itself doesn't do anything | ||
1267 | if (IsModifierKey(ks)) | ||
1268 | return; | ||
1269 | if (event.state) // dont handle modifier with normal key | ||
1270 | return; | ||
1271 | |||
1272 | switch (ks) { | ||
1273 | case XK_Up: | ||
1274 | prevItem(); | ||
1275 | break; | ||
1276 | case XK_Down: | ||
1277 | nextItem(); | ||
1278 | break; | ||
1279 | case XK_Left: | ||
1280 | if (which_press >= 0 && which_press < menuitems.size() && | ||
1281 | m_parent != 0) { | ||
1282 | if (menuitems[which_press]->submenu()) | ||
1283 | menuitems[which_press]->submenu()->menu.window.hide(); | ||
1284 | drawItem(which_press, false, true, true); | ||
1285 | m_parent->grabInputFocus(); | ||
1286 | } | ||
1287 | break; | ||
1288 | case XK_Right: | ||
1289 | if (which_press >= 0 && which_press < menuitems.size() && | ||
1290 | menuitems[which_press]->submenu()) { | ||
1291 | menuitems[which_press]->submenu()->grabInputFocus(); | ||
1292 | menuitems[which_press]->submenu()->which_press = -1; | ||
1293 | menuitems[which_press]->submenu()->nextItem(); | ||
1294 | } | ||
1295 | break; | ||
1296 | case XK_Escape: | ||
1297 | hide(); | ||
1298 | break; | ||
1299 | case XK_Return: | ||
1300 | if (which_press >= 0 && which_press < menuitems.size()) { | ||
1301 | menuitems[which_press]->click(1, event.time); | ||
1302 | itemSelected(1, which_press); | ||
1303 | update(); | ||
1304 | } | ||
1305 | break; | ||
1306 | default: | ||
1307 | break; | ||
1308 | } | ||
1309 | } | ||
1158 | 1310 | ||
1159 | void Menu::reconfigure() { | 1311 | void Menu::reconfigure() { |
1160 | m_need_update = true; // redraw items | 1312 | m_need_update = true; // redraw items |
diff --git a/src/FbTk/Menu.hh b/src/FbTk/Menu.hh index 5dd0eb8..1803241 100644 --- a/src/FbTk/Menu.hh +++ b/src/FbTk/Menu.hh | |||
@@ -22,7 +22,7 @@ | |||
22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
23 | // DEALINGS IN THE SOFTWARE. | 23 | // DEALINGS IN THE SOFTWARE. |
24 | 24 | ||
25 | // $Id: Menu.hh,v 1.15 2003/05/13 00:24:00 fluxgen Exp $ | 25 | // $Id: Menu.hh,v 1.16 2003/07/02 05:26:45 fluxgen Exp $ |
26 | 26 | ||
27 | #ifndef FBTK_MENU_HH | 27 | #ifndef FBTK_MENU_HH |
28 | #define FBTK_MENU_HH | 28 | #define FBTK_MENU_HH |
@@ -85,6 +85,10 @@ public: | |||
85 | virtual void raise(); | 85 | virtual void raise(); |
86 | /// lower this window | 86 | /// lower this window |
87 | virtual void lower(); | 87 | virtual void lower(); |
88 | /// select next item | ||
89 | void nextItem(); | ||
90 | /// select previous item | ||
91 | void prevItem(); | ||
88 | 92 | ||
89 | void disableTitle(); | 93 | void disableTitle(); |
90 | void enableTitle(); | 94 | void enableTitle(); |
@@ -95,14 +99,17 @@ public: | |||
95 | @name event handlers | 99 | @name event handlers |
96 | */ | 100 | */ |
97 | //@{ | 101 | //@{ |
102 | void handleEvent(XEvent &event); | ||
98 | void buttonPressEvent(XButtonEvent &bp); | 103 | void buttonPressEvent(XButtonEvent &bp); |
99 | void buttonReleaseEvent(XButtonEvent &br); | 104 | void buttonReleaseEvent(XButtonEvent &br); |
100 | void motionNotifyEvent(XMotionEvent &mn); | 105 | void motionNotifyEvent(XMotionEvent &mn); |
101 | void enterNotifyEvent(XCrossingEvent &en); | 106 | void enterNotifyEvent(XCrossingEvent &en); |
102 | void leaveNotifyEvent(XCrossingEvent &ce); | 107 | void leaveNotifyEvent(XCrossingEvent &ce); |
103 | void exposeEvent(XExposeEvent &ee); | 108 | void exposeEvent(XExposeEvent &ee); |
109 | void keyPressEvent(XKeyEvent &ke); | ||
104 | //@} | 110 | //@} |
105 | 111 | /// get input focus | |
112 | void grabInputFocus(); | ||
106 | void reconfigure(); | 113 | void reconfigure(); |
107 | /// set label string | 114 | /// set label string |
108 | void setLabel(const char *labelstr); | 115 | void setLabel(const char *labelstr); |
@@ -142,6 +149,7 @@ public: | |||
142 | bool isItemSelected(unsigned int index) const; | 149 | bool isItemSelected(unsigned int index) const; |
143 | bool isItemEnabled(unsigned int index) const; | 150 | bool isItemEnabled(unsigned int index) const; |
144 | static unsigned char alpha() { return s_alpha; } | 151 | static unsigned char alpha() { return s_alpha; } |
152 | static Menu *focused() { return s_focused; } | ||
145 | /// @return menuitem at index | 153 | /// @return menuitem at index |
146 | inline const MenuItem *find(unsigned int index) const { return menuitems[index]; } | 154 | inline const MenuItem *find(unsigned int index) const { return menuitems[index]; } |
147 | inline MenuItem *find(unsigned int index) { return menuitems[index]; } | 155 | inline MenuItem *find(unsigned int index) { return menuitems[index]; } |
@@ -170,6 +178,7 @@ private: | |||
170 | const MenuTheme &m_theme; | 178 | const MenuTheme &m_theme; |
171 | Display *m_display; | 179 | Display *m_display; |
172 | const int m_screen_num; | 180 | const int m_screen_num; |
181 | Window m_prev_focused_window; | ||
173 | Menu *m_parent; | 182 | Menu *m_parent; |
174 | ImageControl &m_image_ctrl; | 183 | ImageControl &m_image_ctrl; |
175 | Menuitems menuitems; | 184 | Menuitems menuitems; |
@@ -206,6 +215,7 @@ private: | |||
206 | std::auto_ptr<Transparent> m_trans; | 215 | std::auto_ptr<Transparent> m_trans; |
207 | Drawable m_root_pm; | 216 | Drawable m_root_pm; |
208 | static unsigned char s_alpha; | 217 | static unsigned char s_alpha; |
218 | static Menu *s_focused; ///< holds current input focused menu, so one can determine if a menu is focused | ||
209 | FbPixmap m_frame_pm; | 219 | FbPixmap m_frame_pm; |
210 | bool m_need_update; | 220 | bool m_need_update; |
211 | }; | 221 | }; |