HaikuDepot: Improve filter UI, fix pkg translations

* The categories and other filter options are orthogonal. Don't
   force the user to choose between real categories and for example
   "installed" or "available" in the same drop-down. I've removed
   (for now) the Options main menu. There are now four small check-marks
   below the filtering drop-downs: Available, Installed, Development and
   Source Code. These enable showing the respective packages in the list
   view. Only "Available" is checked by default. This changes the default
   behavior to show only not-yet-installed packages. This change puts
   the filtering options in one place, the showing of development or
   source code packages is not "hidden somewhere else" anymore. I am not
   so happy with the additional row, however, I am also thinking about
   using icons instead of the checkmarks.
 * Fixed finding the suitable package translation for summary and
   description. For example, WonderBrush now has a German translation.
This commit is contained in:
Stephan Aßmus 2014-09-13 22:27:59 +02:00
parent 8bfb30ceb3
commit ab172803ff
5 changed files with 192 additions and 113 deletions

View File

@ -9,6 +9,7 @@
#include <stdio.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <LayoutBuilder.h>
#include <MenuField.h>
#include <MenuItem.h>
@ -36,15 +37,27 @@ add_categories_to_menu(const CategoryList& categories, BMenu* menu)
}
static BCheckBox*
create_check_box(const char* label, const char* name)
{
BMessage* message = new BMessage(MSG_FILTER_SELECTED);
message->AddString("name", name);
BCheckBox* checkBox = new BCheckBox(label, message);
BFont font;
checkBox->GetFont(&font);
font.SetSize(ceilf(font.Size() * 0.75));
checkBox->SetFont(&font);
return checkBox;
}
FilterView::FilterView()
:
BGroupView("filter view")
BGroupView("filter view", B_VERTICAL)
{
// Contruct category popup
BPopUpMenu* categoryMenu = new BPopUpMenu(B_TRANSLATE("Show"));
fShowField = new BMenuField("category", B_TRANSLATE("Show:"),
categoryMenu);
BPopUpMenu* showMenu = new BPopUpMenu(B_TRANSLATE("Category"));
fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu);
// Construct repository popup
BPopUpMenu* repositoryMenu = new BPopUpMenu(B_TRANSLATE("Depot"));
@ -67,15 +80,34 @@ FilterView::FilterView()
float maxSearchWidth = minSearchWidth * 2;
fSearchTermsText->SetExplicitMaxSize(BSize(maxSearchWidth, B_SIZE_UNSET));
// Construct check boxen
fAvailableCheckBox = create_check_box(
B_TRANSLATE("Available"), "available");
fInstalledCheckBox = create_check_box(
B_TRANSLATE("Installed"), "installed");
fDevelopmentCheckBox = create_check_box(
B_TRANSLATE("Development"), "development");
fSourceCodeCheckBox = create_check_box(
B_TRANSLATE("Source code"), "source code");
// Build layout
BLayoutBuilder::Group<>(this)
.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1.2f)
.Add(fShowField, 0.0f)
.Add(fRepositoryField, 0.0f)
.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
.AddGroup(B_HORIZONTAL)
.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1.2f)
.Add(fShowField, 0.0f)
.Add(fRepositoryField, 0.0f)
.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
.End()
.AddGlue(0.5f)
.Add(fSearchTermsText, 1.0f)
.End()
.AddGroup(B_HORIZONTAL)
.Add(fAvailableCheckBox)
.Add(fInstalledCheckBox)
.Add(fDevelopmentCheckBox)
.Add(fSourceCodeCheckBox)
.AddGlue(0.5f)
.End()
.AddGlue(0.5f)
.Add(fSearchTermsText, 1.0f)
.SetInsets(B_USE_DEFAULT_SPACING)
;
@ -95,6 +127,11 @@ FilterView::AttachedToWindow()
fSearchTermsText->SetTarget(this);
fSearchTermsText->MakeFocus();
fAvailableCheckBox->SetTarget(Window());
fInstalledCheckBox->SetTarget(Window());
fDevelopmentCheckBox->SetTarget(Window());
fSourceCodeCheckBox->SetTarget(Window());
}
@ -138,17 +175,28 @@ FilterView::AdoptModel(const Model& model)
repositoryMenu->AddItem(item);
}
BMenu* categoryMenu = fShowField->Menu();
categoryMenu->RemoveItems(0, categoryMenu->CountItems(), true);
BMenu* showMenu = fShowField->Menu();
showMenu->RemoveItems(0, showMenu->CountItems(), true);
categoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All packages"),
showMenu->AddItem(new BMenuItem(B_TRANSLATE("All categories"),
new BMessage(MSG_CATEGORY_SELECTED)));
categoryMenu->AddItem(new BSeparatorItem());
add_categories_to_menu(model.Categories(), categoryMenu);
categoryMenu->AddItem(new BSeparatorItem());
add_categories_to_menu(model.UserCategories(), categoryMenu);
categoryMenu->AddItem(new BSeparatorItem());
add_categories_to_menu(model.ProgressCategories(), categoryMenu);
categoryMenu->ItemAt(0)->SetMarked(true);
showMenu->AddItem(new BSeparatorItem());
add_categories_to_menu(model.Categories(), showMenu);
showMenu->ItemAt(0)->SetMarked(true);
AdoptCheckmarks(model);
}
void
FilterView::AdoptCheckmarks(const Model& model)
{
fAvailableCheckBox->SetValue(model.ShowAvailablePackages());
fInstalledCheckBox->SetValue(model.ShowInstalledPackages());
fDevelopmentCheckBox->SetValue(model.ShowDevelopPackages());
fSourceCodeCheckBox->SetValue(model.ShowSourcePackages());
}

View File

@ -8,6 +8,7 @@
#include <GroupView.h>
class BCheckBox;
class BMenuField;
class BTextControl;
class Model;
@ -15,6 +16,7 @@ class Model;
enum {
MSG_CATEGORY_SELECTED = 'ctsl',
MSG_FILTER_SELECTED = 'ftsl',
MSG_DEPOT_SELECTED = 'dpsl',
MSG_SEARCH_TERMS_MODIFIED = 'stmd',
};
@ -29,11 +31,17 @@ public:
virtual void MessageReceived(BMessage* message);
virtual void AdoptModel(const Model& model);
virtual void AdoptCheckmarks(const Model& model);
private:
BMenuField* fShowField;
BMenuField* fRepositoryField;
BTextControl* fSearchTermsText;
BCheckBox* fAvailableCheckBox;
BCheckBox* fInstalledCheckBox;
BCheckBox* fDevelopmentCheckBox;
BCheckBox* fSourceCodeCheckBox;
};
#endif // FILTER_VIEW_H

View File

@ -246,7 +246,40 @@ MainWindow::MessageReceived(BMessage* message)
BString name;
if (message->FindString("name", &name) != B_OK)
name = "";
fModel.SetCategory(name);
{
BAutolock locker(fModel.Lock());
fModel.SetCategory(name);
}
_AdoptModel();
break;
}
case MSG_FILTER_SELECTED:
{
BString name;
int32 value;
if (message->FindString("name", &name) != B_OK
|| message->FindInt32("be:value", &value) != B_OK) {
break;
}
{
BAutolock locker(fModel.Lock());
if (name == "available") {
fModel.SetShowAvailablePackages(
value == B_CONTROL_ON);
} else if (name == "installed") {
fModel.SetShowInstalledPackages(
value == B_CONTROL_ON);
} else if (name == "development") {
fModel.SetShowDevelopPackages(
value == B_CONTROL_ON);
} else if (name == "source code") {
fModel.SetShowSourcePackages(
value == B_CONTROL_ON);
} else {
break;
}
}
_AdoptModel();
break;
}
@ -256,7 +289,10 @@ MainWindow::MessageReceived(BMessage* message)
BString name;
if (message->FindString("name", &name) != B_OK)
name = "";
fModel.SetDepot(name);
{
BAutolock locker(fModel.Lock());
fModel.SetDepot(name);
}
_AdoptModel();
break;
}
@ -267,7 +303,10 @@ MainWindow::MessageReceived(BMessage* message)
BString searchTerms;
if (message->FindString("search terms", &searchTerms) != B_OK)
searchTerms = "";
fModel.SetSearchTerms(searchTerms);
{
BAutolock locker(fModel.Lock());
fModel.SetSearchTerms(searchTerms);
}
_AdoptModel();
break;
}
@ -277,6 +316,7 @@ MainWindow::MessageReceived(BMessage* message)
PackageInfo* info;
if (message->FindPointer("package", (void**)&info) == B_OK) {
PackageInfoRef ref(info, true);
BAutolock locker(fModel.Lock());
fModel.SetPackageState(ref, ref->State());
}
break;
@ -356,18 +396,18 @@ MainWindow::_BuildMenu(BMenuBar* menuBar)
new BMessage(MSG_REFRESH_DEPOTS)));
menuBar->AddItem(menu);
menu = new BMenu(B_TRANSLATE("Options"));
fShowDevelopPackagesItem = new BMenuItem(
B_TRANSLATE("Show develop packages"),
new BMessage(MSG_SHOW_DEVELOP_PACKAGES));
menu->AddItem(fShowDevelopPackagesItem);
fShowSourcePackagesItem = new BMenuItem(B_TRANSLATE("Show source packages"),
new BMessage(MSG_SHOW_SOURCE_PACKAGES));
menu->AddItem(fShowSourcePackagesItem);
menuBar->AddItem(menu);
// menu = new BMenu(B_TRANSLATE("Options"));
//
// fShowDevelopPackagesItem = new BMenuItem(
// B_TRANSLATE("Show develop packages"),
// new BMessage(MSG_SHOW_DEVELOP_PACKAGES));
// menu->AddItem(fShowDevelopPackagesItem);
//
// fShowSourcePackagesItem = new BMenuItem(B_TRANSLATE("Show source packages"),
// new BMessage(MSG_SHOW_SOURCE_PACKAGES));
// menu->AddItem(fShowSourcePackagesItem);
//
// menuBar->AddItem(menu);
}
@ -383,8 +423,9 @@ MainWindow::_AdoptModel()
}
BAutolock locker(fModel.Lock());
fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages());
fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages());
// fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages());
// fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages());
fFilterView->AdoptCheckmarks(fModel);
}

View File

@ -303,6 +303,8 @@ Model::Model()
fDepotFilter(""),
fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
fShowAvailablePackages(true),
fShowInstalledPackages(false),
fShowSourcePackages(false),
fShowDevelopPackages(false),
@ -325,39 +327,6 @@ Model::Model()
// get the defined categories and their translated names.
// This should then be used instead of hard-coded
// categories and translations in the app.
// A category for packages that the user installed.
fUserCategories.Add(CategoryRef(new PackageCategory(
BitmapRef(),
B_TRANSLATE("Installed packages"), "installed"), true));
// A category for packages that not yet installed.
fUserCategories.Add(CategoryRef(new PackageCategory(
BitmapRef(),
B_TRANSLATE("Available packages"), "available"), true));
// A category for packages that the user specifically uninstalled.
// For example, a user may have removed packages from a default
// Haiku installation
fUserCategories.Add(CategoryRef(new PackageCategory(
BitmapRef(),
B_TRANSLATE("Uninstalled packages"), "uninstalled"), true));
// A category for all packages that the user has installed or uninstalled.
// Those packages resemble what makes their system different from a
// fresh Haiku installation.
fUserCategories.Add(CategoryRef(new PackageCategory(
BitmapRef(),
B_TRANSLATE("User modified packages"), "modified"), true));
// Two categories to see just the packages which are downloading or
// have updates available
fProgressCategories.Add(CategoryRef(new PackageCategory(
BitmapRef(),
B_TRANSLATE("Downloading"), "downloading"), true));
fProgressCategories.Add(CategoryRef(new PackageCategory(
BitmapRef(),
B_TRANSLATE("Update available"), "updates"), true));
}
@ -370,9 +339,8 @@ Model::~Model()
PackageList
Model::CreatePackageList() const
{
// TODO: Allow to restrict depot, filter by search terms, ...
// Return all packages from all depots.
// Iterate all packages from all depots.
// If configured, restrict depot, filter by search terms, status, name ...
PackageList resultList;
for (int32 i = 0; i < fDepots.CountItems(); i++) {
@ -387,6 +355,8 @@ Model::CreatePackageList() const
const PackageInfoRef& package = packages.ItemAtFast(j);
if (fCategoryFilter->AcceptsPackage(package)
&& fSearchTermsFilter->AcceptsPackage(package)
&& (fShowAvailablePackages || package->State() != NONE)
&& (fShowInstalledPackages || package->State() != ACTIVATED)
&& (fShowSourcePackages || !is_source_package(package))
&& (fShowDevelopPackages || !is_develop_package(package))) {
resultList.Add(package);
@ -439,7 +409,8 @@ Model::SetPackageState(const PackageInfoRef& package, PackageState state)
case UNINSTALLED:
fInstalledPackages.Remove(package);
fActivatedPackages.Remove(package);
fUninstalledPackages.Add(package);
if (!fUninstalledPackages.Contains(package))
fUninstalledPackages.Add(package);
break;
}
@ -457,22 +428,6 @@ Model::SetCategory(const BString& category)
if (category.Length() == 0)
filter = new AnyFilter();
else if (category == "installed")
filter = new ContainedInFilter(fInstalledPackages);
else if (category == "uninstalled")
filter = new ContainedInFilter(fUninstalledPackages);
else if (category == "available") {
filter = new StateFilter(NONE);
// filter = new NotContainedInFilter(&fInstalledPackages,
// &fUninstalledPackages, &fDownloadingPackages, &fUpdateablePackages,
// NULL);
} else if (category == "modified") {
filter = new ContainedInEitherFilter(fInstalledPackages,
fUninstalledPackages);
} else if (category == "downloading")
filter = new ContainedInFilter(fDownloadingPackages);
else if (category == "updates")
filter = new ContainedInFilter(fUpdateablePackages);
else
filter = new CategoryFilter(category);
@ -501,6 +456,20 @@ Model::SetSearchTerms(const BString& searchTerms)
}
void
Model::SetShowAvailablePackages(bool show)
{
fShowAvailablePackages = show;
}
void
Model::SetShowInstalledPackages(bool show)
{
fShowInstalledPackages = show;
}
void
Model::SetShowSourcePackages(bool show)
{
@ -883,23 +852,33 @@ Model::_PopulatePackageInfo(const PackageInfoRef& package, const BMessage& data)
BString foundInfo;
BMessage versions;
BMessage version;
if (data.FindMessage("versions", &versions) == B_OK
&& versions.FindMessage("0", &version)) {
BString languageCode;
if (version.FindString("naturalLanguageCode", &languageCode) == B_OK) {
if (languageCode == fPreferredLanguage) {
BString summary;
if (version.FindString("summary", &summary) == B_OK) {
package->SetShortDescription(summary);
append_word_list(foundInfo, "summary");
}
BString description;
if (version.FindString("description", &description) == B_OK) {
package->SetFullDescription(description);
append_word_list(foundInfo, "description");
}
if (data.FindMessage("versions", &versions) == B_OK) {
// Search a summary and description in the preferred language
int32 index = 0;
while (true) {
BString name;
name << index++;
BMessage version;
if (versions.FindMessage(name, &version) != B_OK)
break;
BString languageCode;
if (version.FindString("naturalLanguageCode",
&languageCode) != B_OK
|| languageCode != fPreferredLanguage) {
continue;
}
BString summary;
if (version.FindString("summary", &summary) == B_OK) {
package->SetShortDescription(summary);
append_word_list(foundInfo, "summary");
}
BString description;
if (version.FindString("description", &description) == B_OK) {
package->SetFullDescription(description);
append_word_list(foundInfo, "description");
}
break;
}
}

View File

@ -66,10 +66,6 @@ public:
const CategoryList& Categories() const
{ return fCategories; }
const CategoryList& UserCategories() const
{ return fUserCategories; }
const CategoryList& ProgressCategories() const
{ return fProgressCategories; }
void SetPackageState(
const PackageInfoRef& package,
@ -79,6 +75,13 @@ public:
void SetCategory(const BString& category);
void SetDepot(const BString& depot);
void SetSearchTerms(const BString& searchTerms);
void SetShowAvailablePackages(bool show);
bool ShowAvailablePackages() const
{ return fShowAvailablePackages; }
void SetShowInstalledPackages(bool show);
bool ShowInstalledPackages() const
{ return fShowInstalledPackages; }
void SetShowSourcePackages(bool show);
bool ShowSourcePackages() const
{ return fShowSourcePackages; }
@ -142,8 +145,6 @@ private:
// TODO: Dynamic categories retrieved from web-app
CategoryList fCategories;
CategoryList fUserCategories;
CategoryList fProgressCategories;
PackageList fInstalledPackages;
PackageList fActivatedPackages;
@ -156,6 +157,8 @@ private:
BString fDepotFilter;
PackageFilterRef fSearchTermsFilter;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;