* Improved Category filtering.

You can now easily hide games on category basis.
Click on a category to cycle through possible checkbox states.

The "ALL" category can now have 3 different states:
  [ ] Not checked = Doesn't display all categories (like before)
  [v] Checked     = Displays all categories (like before)
  [X] Cross       = Displays all categories except forbidden categories.

The other categories now have 4 different states:
  [ ] Not checked = Games containing this category are not filtered (like before)
  [v] Enabled     = All games containing this category will be displayed (like before)
  [X] Forbidden   = All games containing this category will NOT be displayed.
  [+] Required    = All games NOT containing this category will NOT be displayed.
This commit is contained in:
Cyan 2012-06-09 12:20:14 +00:00
parent 74eca1d38b
commit 5a52016f92
12 changed files with 395 additions and 22 deletions

View File

@ -2,8 +2,8 @@
<app version="1">
<name> USB Loader GX</name>
<coder>USB Loader GX Team</coder>
<version>3.0 r1188</version>
<release_date>201205191249</release_date>
<version>3.0 r1189</version>
<release_date>201206090828</release_date>
<!-- // remove this line to enable arguments
<arguments>
<arg>--ios=250</arg>

View File

@ -26,15 +26,17 @@
#define WHITEBOX_RED_SIZE 4
GuiCheckbox::GuiCheckbox(int s)
: GuiButton(30, 30), Checked(false)
: GuiButton(30, 30), Checked(false), MultiStates(false)
{
style = s;
Checksign.SetParent(this);
Cross.SetParent(this);
Plus.SetParent(this);
Blackbox.SetParent(this);
Whitebox.SetParent(this);
Checksign.SetColor((GXColor) {0, 0, 0, 255});
Cross.SetColor((GXColor) {0, 0, 0, 255});
Plus.SetColor((GXColor) {0, 0, 0, 255});
Blackbox.SetColor((GXColor) {0, 0, 0, 255});
Whitebox.SetColor((GXColor) {255, 255, 255, 255});
@ -42,15 +44,17 @@ GuiCheckbox::GuiCheckbox(int s)
}
GuiCheckbox::GuiCheckbox(int w, int h, int s)
: GuiButton(w, h), Checked(false)
: GuiButton(w, h), Checked(false), MultiStates(false)
{
style = s;
Checksign.SetParent(this);
Cross.SetParent(this);
Plus.SetParent(this);
Blackbox.SetParent(this);
Whitebox.SetParent(this);
Checksign.SetColor((GXColor) {0, 0, 0, 255});
Cross.SetColor((GXColor) {0, 0, 0, 255});
Plus.SetColor((GXColor) {0, 0, 0, 255});
Blackbox.SetColor((GXColor) {0, 0, 0, 255});
Whitebox.SetColor((GXColor) {255, 255, 255, 255});
@ -65,6 +69,8 @@ void GuiCheckbox::SetSize(int w, int h)
Checksign.SetPosition(WHITEBOX_RED_SIZE/2, WHITEBOX_RED_SIZE/2);
Cross.SetSize(w-WHITEBOX_RED_SIZE, h-WHITEBOX_RED_SIZE);
Cross.SetPosition(WHITEBOX_RED_SIZE/2, WHITEBOX_RED_SIZE/2);
Plus.SetSize(w-WHITEBOX_RED_SIZE, h-WHITEBOX_RED_SIZE);
Plus.SetPosition(WHITEBOX_RED_SIZE/2, WHITEBOX_RED_SIZE/2);
Blackbox.SetSize(w, h);
Whitebox.SetSize(w-WHITEBOX_RED_SIZE, h-WHITEBOX_RED_SIZE);
Whitebox.SetPosition(WHITEBOX_RED_SIZE/2, WHITEBOX_RED_SIZE/2);
@ -86,7 +92,23 @@ void GuiCheckbox::SetTransparent(bool b)
void GuiCheckbox::SetState(int s, int c)
{
if(s == STATE_CLICKED)
Checked = !Checked;
{
if(MultiStates)
{
if(!Checked)
Checked = !Checked;
else
style++;
if(style == MAX_CHECKBOX_STYLE)
{
Checked = !Checked;
style = CHECKSIGN;
}
}
else
Checked = !Checked;
}
GuiButton::SetState(s, c);
}
@ -96,6 +118,7 @@ void GuiCheckbox::SetAlignment(int h, int v)
GuiButton::SetAlignment(h, v);
Checksign.SetAlignment(h, v);
Cross.SetAlignment(h, v);
Plus.SetAlignment(h, v);
Blackbox.SetAlignment(h, v);
Whitebox.SetAlignment(h, v);
@ -103,24 +126,28 @@ void GuiCheckbox::SetAlignment(int h, int v)
{
Checksign.SetPosition(-WHITEBOX_RED_SIZE/2, Checksign.GetTopPos());
Cross.SetPosition(-WHITEBOX_RED_SIZE/2, Cross.GetTopPos());
Plus.SetPosition(-WHITEBOX_RED_SIZE/2, Plus.GetTopPos());
Whitebox.SetPosition(-WHITEBOX_RED_SIZE/2, Whitebox.GetTopPos());
}
else if(h == ALIGN_CENTER)
{
Checksign.SetPosition(0, Checksign.GetTopPos());
Cross.SetPosition(0, Cross.GetTopPos());
Plus.SetPosition(0, Plus.GetTopPos());
Whitebox.SetPosition(0, Whitebox.GetTopPos());
}
if(v == ALIGN_BOTTOM)
{
Checksign.SetPosition(Checksign.GetLeftPos(), -WHITEBOX_RED_SIZE/2);
Cross.SetPosition(Cross.GetLeftPos(), -WHITEBOX_RED_SIZE/2);
Plus.SetPosition(Plus.GetLeftPos(), -WHITEBOX_RED_SIZE/2);
Whitebox.SetPosition(Whitebox.GetLeftPos(), -WHITEBOX_RED_SIZE/2);
}
else if(v == ALIGN_MIDDLE)
{
Checksign.SetPosition(Checksign.GetLeftPos(), 0);
Cross.SetPosition(Cross.GetLeftPos(), 0);
Plus.SetPosition(Plus.GetLeftPos(), 0);
Whitebox.SetPosition(Whitebox.GetLeftPos(), 0);
}
}
@ -134,6 +161,8 @@ void GuiCheckbox::Draw()
{
if(style == CHECKSIGN)
Checksign.Draw();
else if (style == PLUS)
Plus.Draw();
else
Cross.Draw();
}

View File

@ -28,6 +28,7 @@
#include "GUI/gui_box.hpp"
#include "GUI/gui_cross.hpp"
#include "GUI/gui_checksign.hpp"
#include "GUI/gui_plus.hpp"
class GuiCheckbox : public GuiButton
{
@ -39,21 +40,28 @@ class GuiCheckbox : public GuiButton
void SetClickSize(int w, int h);
void SetAlignment(int h, int v);
void SetChecked(bool c) { LOCK(this); Checked = c; }
void SetStyle(int st) { LOCK(this); style = st; }
void SetMultiStates(bool m) { LOCK(this); MultiStates = m; }
bool IsChecked() const { return Checked; }
int GetStyle() { return style; }
virtual void SetState(int s, int c = -1);
virtual void Draw();
enum
{
CHECKSIGN,
CROSS,
PLUS,
MAX_CHECKBOX_STYLE
};
protected:
GuiChecksign Checksign;
GuiCross Cross;
GuiPlus Plus;
GuiBox Blackbox;
GuiBox Whitebox;
int style;
bool Checked;
bool MultiStates;
};

View File

@ -110,6 +110,37 @@ bool GuiCheckboxBrowser::AddEntrie(const string &text, bool checked)
return true;
}
bool GuiCheckboxBrowser::AddEntrieMultiStates(const string &text, bool checked, int style)
{
LOCK(this);
int currentSize = checkBoxList.size();
textLineList.resize(currentSize+1);
checkBoxList.resize(currentSize+1);
checkBoxList[currentSize] = new GuiCheckbox(30, 30);
checkBoxList[currentSize]->SetParent(this);
checkBoxList[currentSize]->SetChecked(checked);
checkBoxList[currentSize]->SetStyle(style);
checkBoxList[currentSize]->SetMultiStates(true);
checkBoxList[currentSize]->SetAlignment(ALIGN_RIGHT, ALIGN_TOP);
checkBoxList[currentSize]->SetTrigger(&trigA);
checkBoxList[currentSize]->SetClickSize(width-30-scrollBar.GetWidth(), 30);
checkBoxList[currentSize]->Clicked.connect(this, &GuiCheckboxBrowser::OnCheckboxClick);
textLineList[currentSize] = new GuiText(text.c_str(), 18, thColor("r=0 g=0 b=0 a=255 - checkbox browser text color"));
textLineList[currentSize]->SetParent(this);
textLineList[currentSize]->SetAlignment(ALIGN_LEFT, ALIGN_TOP);
textLineList[currentSize]->SetMaxWidth(maxTextWidth, DOTTED);
if(textLineDrawn.size() < (u32) maxSize)
{
textLineDrawn.push_back(textLineList[currentSize]);
checkBoxDrawn.push_back(checkBoxList[currentSize]);
}
return true;
}
void GuiCheckboxBrowser::OnCheckboxClick(GuiButton *sender, int chan, const POINT &pointer)
{
LOCK(this);

View File

@ -37,6 +37,7 @@ class GuiCheckboxBrowser : public GuiElement, public sigslot::has_slots<>
GuiCheckboxBrowser(int w, int h, int maxSize = 7);
virtual ~GuiCheckboxBrowser();
bool AddEntrie(const string &text, bool checked = false);
bool AddEntrieMultiStates(const string &text, bool checked = false, int style = CHECKSIGN);
int GetSelected() const { return pageIndex+selectedItem; }
bool IsChecked(u32 i) { if(i >= checkBoxList.size()) return false; else return checkBoxList[i]->IsChecked(); }
GuiCheckbox *GetCheckbox(u32 i) { if(i >= checkBoxList.size()) return NULL; else return checkBoxList[i]; }
@ -47,6 +48,13 @@ class GuiCheckboxBrowser : public GuiElement, public sigslot::has_slots<>
void Draw();
void Update(GuiTrigger *t);
sigslot::signal2<GuiCheckbox *, int> checkBoxClicked;
enum
{
CHECKSIGN,
CROSS,
PLUS,
MAX_CHECKBOX_STYLE
};
private:
void onListChange(int SelItem, int SelInd);
void OnCheckboxClick(GuiButton *sender, int chan, const POINT &pointer);

53
source/GUI/gui_plus.cpp Normal file
View File

@ -0,0 +1,53 @@
/****************************************************************************
* Copyright (C) 2012
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
***************************************************************************/
#include "gui_plus.hpp"
void GuiPlus::Draw()
{
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
GX_SetVtxDesc(GX_VA_TEX0, GX_NONE);
f32 x1 = GetLeft() + width*0.15f;
f32 x2 = GetLeft() + width*0.5f;
f32 x3 = GetLeft() + width - width*0.15f;
f32 y1 = GetTop() + height*0.15f;
f32 y2 = GetTop() + height*0.5f;
f32 y3 = GetTop() + height - height*0.15f;
int alpha = GetAlpha();
GX_Begin(GX_LINES, GX_VTXFMT0, 4);
GX_Position3f32(x2, y1, 0.0f);
GX_Color4u8(color.r, color.g, color.b, alpha);
GX_Position3f32(x2, y3, 0.0f);
GX_Color4u8(color.r, color.g, color.b, alpha);
GX_Position3f32(x1, y2, 0.0f);
GX_Color4u8(color.r, color.g, color.b, alpha);
GX_Position3f32(x3, y2, 0.0f);
GX_Color4u8(color.r, color.g, color.b, alpha);
GX_End();
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
}

43
source/GUI/gui_plus.hpp Normal file
View File

@ -0,0 +1,43 @@
/****************************************************************************
* Copyright (C) 2012
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
***************************************************************************/
#ifndef GUIPLUS_HPP_
#define GUIPLUS_HPP_
#include "GUI/gui.h"
class GuiPlus : public GuiElement
{
public:
GuiPlus() : Linewidth(2.0f) { color = (GXColor) {0, 0, 0, 255}; GX_SetLineWidth((u8) (Linewidth*6.0f), 0); }
//! Max line width is 42.5 pixel
void SetLinewidth(float w) { LOCK(this); Linewidth = w; GX_SetLineWidth((u8) (Linewidth*6.0f), 0); }
void SetColor(const GXColor c) { LOCK(this); color = c; }
void SetSize(int w, int h) { LOCK(this); width = w; height = h; }
void Draw();
protected:
GXColor color;
float Linewidth;
};
#endif

View File

@ -27,7 +27,8 @@
#include "language/gettext.h"
CategorySwitchPrompt::CategorySwitchPrompt()
: CategoryPrompt(tr("Show Categories")), oldSetting(Settings.EnabledCategories)
: CategoryPrompt(tr("Show Categories")), oldSettingEnabled(Settings.EnabledCategories),
oldSettingRequired(Settings.RequiredCategories), oldSettingForbidden(Settings.ForbiddenCategories)
{
browser->checkBoxClicked.connect(this, &CategorySwitchPrompt::OnCheckboxClick);
browserRefresh.connect(this, &CategorySwitchPrompt::onBrowserRefresh);
@ -38,7 +39,9 @@ CategorySwitchPrompt::CategorySwitchPrompt()
void CategorySwitchPrompt::onResetChanges()
{
Settings.EnabledCategories = oldSetting;
Settings.EnabledCategories = oldSettingEnabled;
Settings.RequiredCategories = oldSettingRequired;
Settings.ForbiddenCategories = oldSettingForbidden;
GameCategories.Load(Settings.ConfigPath);
}
@ -49,7 +52,9 @@ void CategorySwitchPrompt::onBrowserRefresh()
do
{
bool checked = false;
int style = CHECKSIGN;
// Verify the Enabled Categories [v]
for(u32 i = 0; i < Settings.EnabledCategories.size(); ++i)
{
if(Settings.EnabledCategories[i] == GameCategories.CategoryList.getCurrentID())
@ -58,8 +63,32 @@ void CategorySwitchPrompt::onBrowserRefresh()
break;
}
}
browser->AddEntrie(tr(GameCategories.CategoryList.getCurrentName().c_str()), checked);
// Verify the Forbidden Categories [X]
if(!checked)
for(u32 i = 0; i < Settings.ForbiddenCategories.size(); ++i)
{
if(Settings.ForbiddenCategories[i] == GameCategories.CategoryList.getCurrentID())
{
checked = true;
style = CROSS;
break;
}
}
// Verify the Required Categories [+]
if(!checked)
for(u32 i = 0; i < Settings.RequiredCategories.size(); ++i)
{
if(Settings.RequiredCategories[i] == GameCategories.CategoryList.getCurrentID())
{
checked = true;
style = PLUS;
break;
}
}
browser->AddEntrieMultiStates(tr(GameCategories.CategoryList.getCurrentName().c_str()), checked, style);
}
while(GameCategories.CategoryList.goToNext());
@ -74,22 +103,69 @@ void CategorySwitchPrompt::OnCheckboxClick(GuiCheckbox *checkBox, int index)
GameCategories.CategoryList.goToNext();
u32 i;
for(i = 0; i < Settings.EnabledCategories.size(); ++i)
if(!checkBox->IsChecked())
{
if(Settings.EnabledCategories[i] == GameCategories.CategoryList.getCurrentID())
// Remove from Required
for(i = 0; i < Settings.RequiredCategories.size(); ++i)
{
if(!checkBox->IsChecked())
if(Settings.RequiredCategories[i] == GameCategories.CategoryList.getCurrentID())
{
Settings.EnabledCategories.erase(Settings.EnabledCategories.begin()+i);
Settings.RequiredCategories.erase(Settings.RequiredCategories.begin()+i);
markChanged();
break;
}
break;
}
}
if(i == Settings.EnabledCategories.size() && checkBox->IsChecked())
else if(checkBox->GetStyle() == CHECKSIGN)
{
// Add to Enabled
Settings.EnabledCategories.push_back(GameCategories.CategoryList.getCurrentID());
markChanged();
}
else if(checkBox->GetStyle() == CROSS)
{
// Remove from Enabled
for(i = 0; i < Settings.EnabledCategories.size(); ++i)
{
if(Settings.EnabledCategories[i] == GameCategories.CategoryList.getCurrentID())
{
Settings.EnabledCategories.erase(Settings.EnabledCategories.begin()+i);
break;
}
}
// Add to Forbidden
Settings.ForbiddenCategories.push_back(GameCategories.CategoryList.getCurrentID());
markChanged();
}
else if(checkBox->GetStyle() == PLUS && index > 0)
{
// Remove from Forbidden
for(i = 0; i < Settings.ForbiddenCategories.size(); ++i)
{
if(Settings.ForbiddenCategories[i] == GameCategories.CategoryList.getCurrentID())
{
Settings.ForbiddenCategories.erase(Settings.ForbiddenCategories.begin()+i);
break;
}
}
// Add to Required
Settings.RequiredCategories.push_back(GameCategories.CategoryList.getCurrentID());
markChanged();
}
// Override Style cycling for category "All"
if(index == 0 && checkBox->GetStyle() == PLUS)
{
checkBox->SetStyle(CHECKSIGN);
checkBox->SetChecked(false);
for(i = 0; i < Settings.ForbiddenCategories.size(); ++i)
{
if(Settings.ForbiddenCategories[i] == GameCategories.CategoryList.getCurrentID())
{
Settings.ForbiddenCategories.erase(Settings.ForbiddenCategories.begin()+i);
markChanged();
break;
}
}
}
}

View File

@ -35,7 +35,16 @@ class CategorySwitchPrompt : public CategoryPrompt
void onBrowserRefresh();
void onResetChanges();
const std::vector<u32> oldSetting;
const std::vector<u32> oldSettingEnabled;
const std::vector<u32> oldSettingRequired;
const std::vector<u32> oldSettingForbidden;
};
enum
{
CHECKSIGN,
CROSS,
PLUS,
MAX_CHECKBOX_STYLE
};
#endif

View File

@ -133,6 +133,8 @@ void CSettings::SetDefault()
ClockFontScaleFactor = 1.0f; // Scale of 1 to prevent misaligned clock.
EnabledCategories.resize(1);
EnabledCategories[0] = 0;
RequiredCategories.resize(0);
ForbiddenCategories.resize(0);
Wiinnertag = OFF;
SelectedGame = 0;
GameListOffset = 0;
@ -333,6 +335,22 @@ bool CSettings::Save()
fprintf(file, ",");
}
fprintf(file, "\n");
fprintf(file, "RequiredCategories = ");
for(u32 i = 0; i < RequiredCategories.size(); ++i)
{
fprintf(file, "%i", RequiredCategories[i]);
if(i+1 < RequiredCategories.size())
fprintf(file, ",");
}
fprintf(file, "\n");
fprintf(file, "ForbiddenCategories = ");
for(u32 i = 0; i < ForbiddenCategories.size(); ++i)
{
fprintf(file, "%i", ForbiddenCategories[i]);
if(i+1 < ForbiddenCategories.size())
fprintf(file, ",");
}
fprintf(file, "\n");
fprintf(file, "Wiinnertag = %d\n", Wiinnertag);
fprintf(file, "WiinnertagPath = %s\n", WiinnertagPath);
fprintf(file, "SelectedGame = %d\n", SelectedGame);
@ -940,6 +958,44 @@ bool CSettings::SetSetting(char *name, char *value)
}
return true;
}
else if (strcmp(name, "RequiredCategories") == 0)
{
RequiredCategories.clear();
char * strTok = strtok(value, ",");
while (strTok != NULL)
{
u32 id = atoi(strTok);
u32 i;
for(i = 0; i < RequiredCategories.size(); ++i)
{
if(RequiredCategories[i] == id)
break;
}
if(i == RequiredCategories.size())
RequiredCategories.push_back(id);
strTok = strtok(NULL,",");
}
return true;
}
else if (strcmp(name, "ForbiddenCategories") == 0)
{
ForbiddenCategories.clear();
char * strTok = strtok(value, ",");
while (strTok != NULL)
{
u32 id = atoi(strTok);
u32 i;
for(i = 0; i < ForbiddenCategories.size(); ++i)
{
if(ForbiddenCategories[i] == id)
break;
}
if(i == ForbiddenCategories.size())
ForbiddenCategories.push_back(id);
strTok = strtok(NULL,",");
}
return true;
}
return false;
}

View File

@ -137,6 +137,8 @@ class CSettings
short GameListOffset;
short sneekVideoPatch;
std::vector<u32> EnabledCategories;
std::vector<u32> RequiredCategories;
std::vector<u32> ForbiddenCategories;
u8 EntryIOS;
short NandEmuMode;
short NandEmuChanMode;

View File

@ -229,14 +229,72 @@ void GameList::InternalFilterList(std::vector<struct discHdr> &FullList)
//! Category filter
u32 n;
for(n = 0; n < Settings.EnabledCategories.size(); ++n)
enum { DISABLED, ENABLED, HIDEFORBIDDEN };
int allType = DISABLED;
// verify the display mode for category "All"
for(u32 n = 0; n < Settings.EnabledCategories.size(); ++n)
{
if(GameCategories.isInCategory((char *) header->id, Settings.EnabledCategories[n]))
if(Settings.EnabledCategories[n] == 0)
{
allType = ENABLED; // All = Enabled
break;
}
}
if(n == Settings.EnabledCategories.size())
continue;
for(u32 n = 0; n < Settings.ForbiddenCategories.size(); ++n)
{
if(Settings.ForbiddenCategories[n] == 0)
{
allType = HIDEFORBIDDEN; // All = Enabled but hide Forbidden categories
break;
}
}
if(allType == DISABLED)
{
// Remove TitleID if it contains a forbidden categories
for(n = 0; n < Settings.ForbiddenCategories.size(); ++n)
{
if(GameCategories.isInCategory((char *) header->id, Settings.ForbiddenCategories[n]))
break;
}
if(n < Settings.ForbiddenCategories.size())
continue;
// Remove TitleID is it doesn't contain a required categories
for(n = 0; n < Settings.RequiredCategories.size(); ++n)
{
if(!GameCategories.isInCategory((char *) header->id, Settings.RequiredCategories[n]))
break;
}
if(n < Settings.RequiredCategories.size())
continue;
// If there's no required categories, verify if the TitleID should be kept or removed
if(Settings.RequiredCategories.size() == 0)
{
for(n = 0; n < Settings.EnabledCategories.size(); ++n)
{
if(GameCategories.isInCategory((char *) header->id, Settings.EnabledCategories[n]))
break;
}
if(n == Settings.EnabledCategories.size())
continue;
}
}
if(allType == HIDEFORBIDDEN)
{
// Remove TitleID if it contains a forbidden categories
for(n = 0; n < Settings.ForbiddenCategories.size(); ++n)
{
if(GameCategories.isInCategory((char *) header->id, Settings.ForbiddenCategories[n]))
if(Settings.ForbiddenCategories[n] >0)
break;
}
if(n < Settings.ForbiddenCategories.size())
continue;
}
FilteredList.push_back(header);
}
}