From 76117663ff9508605bf575efc44f463da38590cb Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Sun, 11 Aug 2013 23:46:04 +0200 Subject: [PATCH] Converted the context menu to a Layer This fixes a bug with the captured background being wrong when using double buffering. Also it ensures that for example the clock in the status bar is updated when the context menu is open. --- src/Makefile.am | 4 +- src/contextmenu.cpp | 181 ++++++++++++++++++++++++++++++++++++++++++++ src/contextmenu.h | 43 +++++++++++ src/gmenu2x.cpp | 160 +-------------------------------------- src/gmenu2x.h | 15 ++-- src/menu.cpp | 4 +- 6 files changed, 238 insertions(+), 169 deletions(-) create mode 100644 src/contextmenu.cpp create mode 100644 src/contextmenu.h diff --git a/src/Makefile.am b/src/Makefile.am index 0ce802a..70a635d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,7 @@ gmenu2x_SOURCES = font.cpp button.cpp cpu.cpp dirdialog.cpp filedialog.cpp \ utilities.cpp wallpaperdialog.cpp \ browsedialog.cpp buttonbox.cpp dialog.cpp \ imageio.cpp powersaver.cpp monitor.cpp mediamonitor.cpp clock.cpp \ - helppopup.cpp background.cpp battery.cpp + helppopup.cpp contextmenu.cpp background.cpp battery.cpp noinst_HEADERS = font.h button.h cpu.h dirdialog.h \ filedialog.h filelister.h gmenu2x.h gp2x.h iconbutton.h imagedialog.h \ @@ -27,7 +27,7 @@ noinst_HEADERS = font.h button.h cpu.h dirdialog.h \ touchscreen.h translator.h utilities.h wallpaperdialog.h \ browsedialog.h buttonbox.h dialog.h \ imageio.h powersaver.h monitor.h mediamonitor.h clock.h \ - layer.h helppopup.h background.h battery.h + layer.h helppopup.h contextmenu.h background.h battery.h AM_CFLAGS= @CFLAGS@ @SDL_CFLAGS@ diff --git a/src/contextmenu.cpp b/src/contextmenu.cpp new file mode 100644 index 0000000..acfdde9 --- /dev/null +++ b/src/contextmenu.cpp @@ -0,0 +1,181 @@ +// Various authors. +// License: GPL version 2 or later. + +#include "contextmenu.h" + +#include "delegate.h" +#include "gmenu2x.h" +#include "linkapp.h" +#include "menu.h" +#include "utilities.h" + +#include + + +struct ContextMenu::MenuOption { + MenuOption(std::string text, function_t action) + : text(text), action(action) {} + std::string text; + function_t action; +}; + +ContextMenu::ContextMenu(GMenu2X &gmenu2x, Menu &menu) + : gmenu2x(gmenu2x) + , menu(menu) + , fadeAlpha(0) + , selected(0) +{ + Translator &tr = gmenu2x.tr; + Font *font = gmenu2x.font; + LinkApp* app = menu.selLinkApp(); + + // Init menu options: + + options.push_back(std::make_shared( + tr.translate("Add link in $1", menu.selSection().c_str(), NULL), + std::bind(&GMenu2X::addLink, &gmenu2x))); + + if (app && !app->getManual().empty()) { + options.push_back(std::make_shared( + tr.translate("Show manual of $1", app->getTitle().c_str(), NULL), + std::bind(&GMenu2X::showManual, &gmenu2x))); + } + + if (app && app->isEditable()) { + + /* FIXME(percuei): This permits to mask the "Edit link" entry + * on the contextual menu in case CPUFREQ support is + * not compiled in and the link corresponds to an OPK. + * This is not a good idea as it'll break things if + * a new config option is added to the contextual menu. + */ +#if defined(HAVE_LIBOPK) && !defined(ENABLE_CPUFREQ) + if (!app->isOpk() || !app->getSelectorDir().empty()) +#endif + { + options.push_back(std::make_shared( + tr.translate("Edit $1", app->getTitle().c_str(), NULL), + std::bind(&GMenu2X::editLink, &gmenu2x))); + } +#ifdef HAVE_LIBOPK + if (!app->isOpk()) +#endif + { + options.push_back(std::make_shared( + tr.translate("Delete $1 link", app->getTitle().c_str(), NULL), + std::bind(&GMenu2X::deleteLink, &gmenu2x))); + } + } + + options.push_back(std::make_shared( + tr["Add section"], + std::bind(&GMenu2X::addSection, &gmenu2x))); + options.push_back(std::make_shared( + tr["Rename section"], + std::bind(&GMenu2X::renameSection, &gmenu2x))); + options.push_back(std::make_shared( + tr["Delete section"], + std::bind(&GMenu2X::deleteSection, &gmenu2x))); + options.push_back(std::make_shared( + tr["Scan for applications and games"], + std::bind(&GMenu2X::scanner, &gmenu2x))); + + // Compute bounding box. + int w = 0; + for (auto option : options) { + w = std::max(w, font->getTextWidth(option->text)); + } + w += 23; + const int h = (font->getHeight() + 2) * options.size() + 8; + box = { + static_cast((gmenu2x.resX - w) / 2), + static_cast((gmenu2x.resY - h) / 2), + static_cast(w), + static_cast(h) + }; + + // Init background fade animation. + tickStart = SDL_GetTicks(); + startAnimating(); +} + +void ContextMenu::runAnimations() +{ + if (fadeAlpha < 200) { + const long tickNow = SDL_GetTicks(); + fadeAlpha = intTransition(0, 200, tickStart, 500, tickNow); + if (fadeAlpha == 200) { + stopAnimating(); + } + } +} + +void ContextMenu::paint(Surface &s) +{ + runAnimations(); + + Font *font = gmenu2x.font; + + // Darken background. + s.box(0, 0, gmenu2x.resX, gmenu2x.resY, 0, 0, 0, fadeAlpha); + + // Draw popup box. + s.box(box, gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_BG]); + s.rectangle(box.x + 2, box.y + 2, box.w - 4, box.h - 4, + gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_BORDER]); + + // Draw selection background. + const int h = font->getHeight(); + SDL_Rect selbox = { + static_cast(box.x + 4), + static_cast(box.y + 4 + (h + 2) * selected), + static_cast(box.w - 8), + static_cast(h + 2) + }; + s.box(selbox, gmenu2x.skinConfColors[COLOR_MESSAGE_BOX_SELECTION]); + + // List options. + for (uint i = 0; i < options.size(); i++) { + s.write(font, options[i]->text, box.x + 12, box.y + 5 + (h + 2) * i, + Font::HAlignLeft, Font::VAlignTop); + } +} + +bool ContextMenu::handleButtonPress(InputManager::Button button) { + switch (button) { + case InputManager::MENU: + dismiss(); + break; + case InputManager::UP: + selected = std::max(0, selected - 1); + break; + case InputManager::DOWN: + selected = std::min((int)options.size() - 1, selected + 1); + break; + case InputManager::ACCEPT: + options[selected]->action(); + dismiss(); + break; + default: + break; + } + return true; +} + +bool ContextMenu::handleTouchscreen(Touchscreen &ts) { + if (ts.inRect(box)) { + int i = std::max(0, std::min(static_cast(options.size()) - 1, + (ts.getY() - (box.y + 4)) / (gmenu2x.font->getHeight() + 2))); + if (ts.released()) { + options[i]->action(); + dismiss(); + } else if (ts.pressed()) { + selected = i; + } + } else { + if (ts.released()) { + dismiss(); + } + } + return true; +} diff --git a/src/contextmenu.h b/src/contextmenu.h new file mode 100644 index 0000000..bb2b2be --- /dev/null +++ b/src/contextmenu.h @@ -0,0 +1,43 @@ +// Various authors. +// License: GPL version 2 or later. + +#ifndef __CONTEXTMENU_H__ +#define __CONTEXTMENU_H__ + +#include "layer.h" + +#include +#include + +class GMenu2X; +class Menu; + + +/** + * A popup dialog containing action on the current section or link. + */ +class ContextMenu : public Layer { +public: + ContextMenu(GMenu2X &gmenu2x, Menu &menu); + + // Layer implementation: + virtual void paint(Surface &s); + virtual bool handleButtonPress(InputManager::Button button); + virtual bool handleTouchscreen(Touchscreen &ts); + +private: + struct MenuOption; + + void runAnimations(); + + GMenu2X &gmenu2x; + Menu &menu; + std::vector> options; + SDL_Rect box; + + int fadeAlpha; + int selected; + long tickStart; +}; + +#endif // __CONTEXTMENU_H__ diff --git a/src/gmenu2x.cpp b/src/gmenu2x.cpp index 3b597b0..dfea743 100644 --- a/src/gmenu2x.cpp +++ b/src/gmenu2x.cpp @@ -81,13 +81,6 @@ #include -struct MenuOption { - MenuOption(std::string text, function_t action) - : text(text), action(action) {} - std::string text; - function_t action; -}; - using namespace std; #ifndef DEFAULT_WALLPAPER_PATH @@ -840,157 +833,8 @@ void GMenu2X::showManual() { menu->selLinkApp()->showManual(); } -void GMenu2X::contextMenu() { - vector> options; - LinkApp* app = menu->selLinkApp(); - - options.push_back(make_shared( - tr.translate("Add link in $1", menu->selSection().c_str(), NULL), - BIND(&GMenu2X::addLink))); - - if (app && !app->getManual().empty()) { - options.push_back(make_shared( - tr.translate("Show manual of $1", app->getTitle().c_str(), NULL), - BIND(&GMenu2X::showManual))); - } - - if (app && app->isEditable()) { - - /* FIXME(percuei): This permits to mask the "Edit link" entry - * on the contextual menu in case CPUFREQ support is - * not compiled in and the link corresponds to an OPK. - * This is not a good idea as it'll break things if - * a new config option is added to the contextual menu. - */ -#if defined(HAVE_LIBOPK) && !defined(ENABLE_CPUFREQ) - if (!app->isOpk() || !app->getSelectorDir().empty()) -#endif - { - options.push_back(make_shared( - tr.translate("Edit $1", app->getTitle().c_str(), NULL), - BIND(&GMenu2X::editLink))); - } -#ifdef HAVE_LIBOPK - if (!app->isOpk()) -#endif - { - options.push_back(make_shared( - tr.translate("Delete $1 link", app->getTitle().c_str(), NULL), - BIND(&GMenu2X::deleteLink))); - } - } - - options.push_back(make_shared( - tr["Add section"], BIND(&GMenu2X::addSection))); - options.push_back(make_shared( - tr["Rename section"], BIND(&GMenu2X::renameSection))); - options.push_back(make_shared( - tr["Delete section"], BIND(&GMenu2X::deleteSection))); - options.push_back(make_shared( - tr["Scan for applications and games"], BIND(&GMenu2X::scanner))); - - const int h = font->getHeight(); - SDL_Rect box; - box.h = (h + 2) * options.size() + 8; - box.w = 0; - for (auto option : options) { - box.w = max(box.w, static_cast(font->getTextWidth(option->text))); - } - box.w += 23; - box.x = halfX - box.w / 2; - box.y = halfY - box.h / 2; - - SDL_Rect selbox = { - static_cast(box.x + 4), - 0, - static_cast(box.w - 8), - static_cast(h + 2) - }; - const long tickStart = SDL_GetTicks(); - - Surface bg(s); - /*//Darken background - bg.box(0, 0, resX, resY, 0,0,0,150); - bg.box(box.x, box.y, box.w, box.h, skinConfColors["messageBoxBg"]); - bg.rectangle( box.x+2, box.y+2, box.w-4, box.h-4, skinConfColors["messageBoxBorder"] );*/ - - uint fadeAlpha = 0; - int sel = 0; - bool close = false; - while (!close) { - const long tickNow = SDL_GetTicks(); - - selbox.y = box.y + 4 + (h + 2) * sel; - bg.blit(s, 0, 0); - - if (fadeAlpha < 200) - fadeAlpha = intTransition(0, 200, tickStart, 500, tickNow); - s->box(0, 0, resX, resY, 0, 0, 0, fadeAlpha); - s->box(box.x, box.y, box.w, box.h, skinConfColors[COLOR_MESSAGE_BOX_BG]); - s->rectangle(box.x + 2, box.y + 2, box.w - 4, box.h - 4, - skinConfColors[COLOR_MESSAGE_BOX_BORDER]); - - //draw selection rect - s->box(selbox, skinConfColors[COLOR_MESSAGE_BOX_SELECTION]); - for (uint i = 0; i < options.size(); i++) - s->write(font, options[i]->text, box.x + 12, box.y + 5 + (h + 2) * i, - Font::HAlignLeft, Font::VAlignTop); - s->flip(); - - //touchscreen - if (ts.available()) { - ts.poll(); - if (ts.released()) { - if (!ts.inRect(box)) - close = true; - else if (ts.getX() >= selbox.x - && ts.getX() <= selbox.x + selbox.w) - for (uint i = 0; i < options.size(); i++) { - selbox.y = box.y + 4 + (h + 2) * i; - if (ts.getY() >= selbox.y - && ts.getY() <= selbox.y + selbox.h) { - options[i]->action(); - close = true; - i = options.size(); - } - } - } else if (ts.pressed() && ts.inRect(box)) { - for (uint i = 0; i < options.size(); i++) { - selbox.y = box.y + 4 + (h + 2) * i; - if (ts.getY() >= selbox.y - && ts.getY() <= selbox.y + selbox.h) { - sel = i; - i = options.size(); - } - } - } - } - - InputManager::ButtonEvent event; - if (fadeAlpha < 200) { - if (!input.pollEvent(&event) || event.state != InputManager::PRESSED) continue; - } else { - event.button = input.waitForPressedButton(); - } - - switch(event.button) { - case InputManager::MENU: - close = true; - break; - case InputManager::UP: - sel = std::max(0, sel - 1); - break; - case InputManager::DOWN: - sel = std::min((int)options.size() - 1, sel + 1); - break; - case InputManager::ACCEPT: - options[sel]->action(); - close = true; - break; - default: - break; - } - } +void GMenu2X::showContextMenu() { + layers.push_back(make_shared(*this, *menu)); } void GMenu2X::changeWallpaper() { diff --git a/src/gmenu2x.h b/src/gmenu2x.h index 83b0bfa..8d36428 100644 --- a/src/gmenu2x.h +++ b/src/gmenu2x.h @@ -21,6 +21,7 @@ #ifndef GMENU2X_H #define GMENU2X_H +#include "contextmenu.h" #include "surfacecollection.h" #include "translator.h" #include "touchscreen.h" @@ -90,10 +91,6 @@ private: #endif void browsePath(const std::string &path, std::vector* directories, std::vector* files); /*! - Starts the scanning of the nand and sd filesystems, searching for dge and gpu files and creating the links in 2 dedicated sections. - */ - void scanner(); - /*! Performs the actual scan in the given path and populates the files vector with the results. The creation of the links is not performed here. @see scanner */ @@ -119,8 +116,6 @@ private: void initFont(); void initMenu(); - void showManual(); - public: GMenu2X(); ~GMenu2X(); @@ -156,12 +151,18 @@ public: //Status functions void main(); + /** + * Starts the scanning of the nand and sd filesystems, searching for dge + * and gpu files and creating the links in 2 dedicated sections. + */ + void scanner(); + void showContextMenu(); void showHelpPopup(); + void showManual(); void showSettings(); void skinMenu(); void about(); void viewLog(); - void contextMenu(); void changeWallpaper(); #ifdef ENABLE_CPUFREQ diff --git a/src/menu.cpp b/src/menu.cpp index b372277..055d834 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -101,7 +101,7 @@ Menu::Menu(GMenu2X *gmenu2x, Touchscreen &ts) orderLinks(); btnContextMenu->setPosition(gmenu2x->resX - 38, gmenu2x->bottomBarIconY); - btnContextMenu->setAction(std::bind(&GMenu2X::contextMenu, gmenu2x)); + btnContextMenu->setAction(std::bind(&GMenu2X::showContextMenu, gmenu2x)); } Menu::~Menu() { @@ -306,7 +306,7 @@ bool Menu::handleButtonPress(InputManager::Button button) { incSectionIndex(); return true; case InputManager::MENU: - gmenu2x->contextMenu(); + gmenu2x->showContextMenu(); return true; default: return false;