mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-11-22 17:51:34 +02:00
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.
This commit is contained in:
parent
945e29986a
commit
76117663ff
@ -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@
|
||||
|
||||
|
181
src/contextmenu.cpp
Normal file
181
src/contextmenu.cpp
Normal file
@ -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 <algorithm>
|
||||
|
||||
|
||||
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<MenuOption>(
|
||||
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<MenuOption>(
|
||||
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<MenuOption>(
|
||||
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<MenuOption>(
|
||||
tr.translate("Delete $1 link", app->getTitle().c_str(), NULL),
|
||||
std::bind(&GMenu2X::deleteLink, &gmenu2x)));
|
||||
}
|
||||
}
|
||||
|
||||
options.push_back(std::make_shared<MenuOption>(
|
||||
tr["Add section"],
|
||||
std::bind(&GMenu2X::addSection, &gmenu2x)));
|
||||
options.push_back(std::make_shared<MenuOption>(
|
||||
tr["Rename section"],
|
||||
std::bind(&GMenu2X::renameSection, &gmenu2x)));
|
||||
options.push_back(std::make_shared<MenuOption>(
|
||||
tr["Delete section"],
|
||||
std::bind(&GMenu2X::deleteSection, &gmenu2x)));
|
||||
options.push_back(std::make_shared<MenuOption>(
|
||||
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<Sint16>((gmenu2x.resX - w) / 2),
|
||||
static_cast<Sint16>((gmenu2x.resY - h) / 2),
|
||||
static_cast<Uint16>(w),
|
||||
static_cast<Uint16>(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<Sint16>(box.x + 4),
|
||||
static_cast<Sint16>(box.y + 4 + (h + 2) * selected),
|
||||
static_cast<Uint16>(box.w - 8),
|
||||
static_cast<Uint16>(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<int>(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;
|
||||
}
|
43
src/contextmenu.h
Normal file
43
src/contextmenu.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Various authors.
|
||||
// License: GPL version 2 or later.
|
||||
|
||||
#ifndef __CONTEXTMENU_H__
|
||||
#define __CONTEXTMENU_H__
|
||||
|
||||
#include "layer.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <memory>
|
||||
|
||||
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<std::shared_ptr<MenuOption>> options;
|
||||
SDL_Rect box;
|
||||
|
||||
int fadeAlpha;
|
||||
int selected;
|
||||
long tickStart;
|
||||
};
|
||||
|
||||
#endif // __CONTEXTMENU_H__
|
160
src/gmenu2x.cpp
160
src/gmenu2x.cpp
@ -81,13 +81,6 @@
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
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<shared_ptr<MenuOption>> options;
|
||||
LinkApp* app = menu->selLinkApp();
|
||||
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
tr.translate("Add link in $1", menu->selSection().c_str(), NULL),
|
||||
BIND(&GMenu2X::addLink)));
|
||||
|
||||
if (app && !app->getManual().empty()) {
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
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<MenuOption>(
|
||||
tr.translate("Edit $1", app->getTitle().c_str(), NULL),
|
||||
BIND(&GMenu2X::editLink)));
|
||||
}
|
||||
#ifdef HAVE_LIBOPK
|
||||
if (!app->isOpk())
|
||||
#endif
|
||||
{
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
tr.translate("Delete $1 link", app->getTitle().c_str(), NULL),
|
||||
BIND(&GMenu2X::deleteLink)));
|
||||
}
|
||||
}
|
||||
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
tr["Add section"], BIND(&GMenu2X::addSection)));
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
tr["Rename section"], BIND(&GMenu2X::renameSection)));
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
tr["Delete section"], BIND(&GMenu2X::deleteSection)));
|
||||
options.push_back(make_shared<MenuOption>(
|
||||
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<Uint16>(font->getTextWidth(option->text)));
|
||||
}
|
||||
box.w += 23;
|
||||
box.x = halfX - box.w / 2;
|
||||
box.y = halfY - box.h / 2;
|
||||
|
||||
SDL_Rect selbox = {
|
||||
static_cast<Sint16>(box.x + 4),
|
||||
0,
|
||||
static_cast<Uint16>(box.w - 8),
|
||||
static_cast<Uint16>(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<ContextMenu>(*this, *menu));
|
||||
}
|
||||
|
||||
void GMenu2X::changeWallpaper() {
|
||||
|
@ -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<std::string>* directories, std::vector<std::string>* 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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user