From 5a2da01a5b1d59bae3d1f00132230835e6803301 Mon Sep 17 00:00:00 2001 From: Simon Robertshaw Date: Sat, 4 Aug 2012 20:55:59 +0100 Subject: [PATCH] Tags, fixes #55 --- src/client/Client.cpp | 49 ++++++++++++++++ src/client/Client.h | 1 + src/interface/Appearance.cpp | 9 ++- src/interface/Appearance.h | 2 + src/interface/Button.cpp | 39 ++++++++----- src/search/SearchController.cpp | 14 ++++- src/search/SearchController.h | 2 +- src/search/SearchModel.cpp | 58 ++++++++++++++++--- src/search/SearchModel.h | 4 ++ src/search/SearchView.cpp | 99 ++++++++++++++++++++++++++++++++- src/search/SearchView.h | 3 + 11 files changed, 252 insertions(+), 28 deletions(-) diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 2684689f4..5d1e43963 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -1262,6 +1262,55 @@ std::vector * Client::GetComments(int saveID, int start, int count return commentArray; } +std::vector > * Client::GetTags(int start, int count, string query, int & resultCount) +{ + lastError = ""; + resultCount = 0; + std::vector > * tagArray = new std::vector >(); + std::stringstream urlStream; + char * data; + int dataStatus, dataLength; + urlStream << "http://" << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count; + if(query.length()) + { + urlStream << "&Search_Query="; + if(query.length()) + urlStream << URLEscape(query); + } + + data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength); + if(dataStatus == 200 && data) + { + try + { + std::istringstream dataStream(data); + json::Object objDocument; + json::Reader::Read(objDocument, dataStream); + + json::Number tempCount = objDocument["TagTotal"]; + resultCount = tempCount.Value(); + json::Array tagsArray = objDocument["Tags"]; + for(int j = 0; j < tagsArray.Size(); j++) + { + json::Number tagCount = tagsArray[j]["Count"]; + json::String tag = tagsArray[j]["Tag"]; + tagArray->push_back(std::pair(tag.Value(), tagCount.Value())); + } + } + catch (json::Exception &e) + { + lastError = "Could not read response"; + } + } + else + { + lastError = http_ret_text(dataStatus); + } + if(data) + free(data); + return tagArray; +} + std::vector * Client::SearchSaves(int start, int count, string query, string sort, std::string category, int & resultCount) { lastError = ""; diff --git a/src/client/Client.h b/src/client/Client.h index 453342aeb..bea6ffcd4 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -111,6 +111,7 @@ public: LoginStatus Login(string username, string password, User & user); void ClearThumbnailRequests(); std::vector * SearchSaves(int start, int count, string query, string sort, string category, int & resultCount); + std::vector > * GetTags(int start, int count, string query, int & resultCount); std::vector * GetComments(int saveID, int start, int count); Thumbnail * GetPreview(int saveID, int saveDate); Thumbnail * GetThumbnail(int saveID, int saveDate); diff --git a/src/interface/Appearance.cpp b/src/interface/Appearance.cpp index b504d02b7..45c3592a0 100644 --- a/src/interface/Appearance.cpp +++ b/src/interface/Appearance.cpp @@ -14,18 +14,21 @@ namespace ui HorizontalAlign(AlignCentre), VerticalAlign(AlignMiddle), - BackgroundHover(30, 30, 30), + BackgroundHover(20, 20, 20), BackgroundInactive(0, 0, 0), BackgroundActive(255, 255, 255), - BackgroundDisabled(100, 100, 100), + BackgroundDisabled(10, 10, 10), TextHover(255, 255, 255), TextInactive(255, 255, 255), TextActive(0, 0, 0), + TextDisabled(100, 100, 100), BorderHover(255, 255, 255), BorderInactive(200, 200, 200), - BorderActive(255, 255, 255), + BorderActive(235, 235, 235), + BorderDisabled(100, 100, 100), + Margin(1, 4), icon(NoIcon), diff --git a/src/interface/Appearance.h b/src/interface/Appearance.h index 4767edbee..e6f0a93be 100644 --- a/src/interface/Appearance.h +++ b/src/interface/Appearance.h @@ -40,10 +40,12 @@ namespace ui ui::Colour TextHover; ui::Colour TextInactive; ui::Colour TextActive; + ui::Colour TextDisabled; ui::Colour BorderHover; ui::Colour BorderInactive; ui::Colour BorderActive; + ui::Colour BorderDisabled; ui::Border Margin; diff --git a/src/interface/Button.cpp b/src/interface/Button.cpp index 996c02f18..a9b0606aa 100644 --- a/src/interface/Button.cpp +++ b/src/interface/Button.cpp @@ -86,32 +86,45 @@ void Button::Draw(const Point& screenPos) Graphics * g = ui::Engine::Ref().g; Point Position = screenPos; ui::Colour bgColour(0, 0, 0); + + ui::Colour textColour = Appearance.TextInactive; + ui::Colour borderColour = Appearance.BorderInactive; + ui::Colour backgroundColour = Appearance.BackgroundInactive; + if(Enabled) { if(isButtonDown || (isTogglable && toggle)) { - bgColour = Appearance.BackgroundActive; - g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, Appearance.BackgroundActive.Red, Appearance.BackgroundActive.Green, Appearance.BackgroundActive.Blue, 255); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BorderActive.Red, Appearance.BorderActive.Green, Appearance.BorderActive.Blue, 255); - g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, Appearance.TextActive.Red, Appearance.TextActive.Green, Appearance.TextActive.Blue, 255); + textColour = Appearance.TextActive; + borderColour = Appearance.BorderActive; + backgroundColour = Appearance.BackgroundActive; + } + else if (isMouseInside) + { + textColour = Appearance.TextHover; + borderColour = Appearance.BorderHover; + backgroundColour = Appearance.BackgroundHover; } else { - bgColour = Appearance.BackgroundInactive; - g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, 255); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BorderInactive.Red, Appearance.BorderInactive.Green, Appearance.BorderInactive.Blue, 255); - g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, Appearance.TextInactive.Red, Appearance.TextInactive.Green, Appearance.TextInactive.Blue, 255); + textColour = Appearance.TextInactive; + borderColour = Appearance.BorderInactive; + backgroundColour = Appearance.BackgroundInactive; } } else { - bgColour = Appearance.BackgroundInactive; - g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, 180); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BackgroundDisabled.Red, Appearance.BackgroundDisabled.Green, Appearance.BackgroundDisabled.Blue, Appearance.BackgroundDisabled.Alpha); - g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, 180, 180, 180, 255); + textColour = Appearance.TextDisabled; + borderColour = Appearance.BorderDisabled; + backgroundColour = Appearance.BackgroundDisabled; } - bool iconInvert = (bgColour.Blue + (3*bgColour.Green) + (2*bgColour.Red))>544?true:false; + bgColour = Appearance.BackgroundInactive; + g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha); + + bool iconInvert = (backgroundColour.Blue + (3*backgroundColour.Green) + (2*backgroundColour.Red))>544?true:false; if(Appearance.icon) { diff --git a/src/search/SearchController.cpp b/src/search/SearchController.cpp index f1273548c..cd1e5f07c 100644 --- a/src/search/SearchController.cpp +++ b/src/search/SearchController.cpp @@ -96,11 +96,19 @@ SearchController::~SearchController() delete searchView; } -void SearchController::DoSearch(std::string query) +void SearchController::DoSearch(std::string query, bool now) { nextQuery = query; - nextQueryTime = clock()+(0.6 * CLOCKS_PER_SEC); - nextQueryDone = false; + if(!now) + { + nextQueryTime = clock()+(0.6 * CLOCKS_PER_SEC); + nextQueryDone = false; + } + else + { + nextQueryDone = true; + searchModel->UpdateSaveList(1, nextQuery); + } //searchModel->UpdateSaveList(1, query); } diff --git a/src/search/SearchController.h b/src/search/SearchController.h index 16b4039c7..32ca8b332 100644 --- a/src/search/SearchController.h +++ b/src/search/SearchController.h @@ -30,7 +30,7 @@ public: ~SearchController(); SearchView * GetView() { return searchView; } void Exit(); - void DoSearch(std::string query); + void DoSearch(std::string query, bool now = false); void NextPage(); void PrevPage(); void ChangeSort(); diff --git a/src/search/SearchModel.cpp b/src/search/SearchModel.cpp index 94496737c..63231d724 100644 --- a/src/search/SearchModel.cpp +++ b/src/search/SearchModel.cpp @@ -12,10 +12,16 @@ SearchModel::SearchModel(): updateSaveListFinished(false), saveListLoaded(false), currentPage(1), - resultCount(0) + resultCount(0), + showTags(true) { } +void SearchModel::SetShowTags(bool show) +{ + showTags = show; +} + void * SearchModel::updateSaveListTHelper(void * obj) { return ((SearchModel *)obj)->updateSaveListT(); @@ -23,14 +29,27 @@ void * SearchModel::updateSaveListTHelper(void * obj) void * SearchModel::updateSaveListT() { + void ** information = new void*[2]; + std::string category = ""; if(showFavourite) category = "Favourites"; if(showOwn && Client::Ref().GetAuthUser().ID) category = "by:"+Client::Ref().GetAuthUser().Username; - vector * tempSaveList = Client::Ref().SearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category, resultCount); + information[0] = Client::Ref().SearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category, resultCount); + + if(showTags) + { + int tagResultCount; + information[1] = Client::Ref().GetTags(0, 24, "", tagResultCount); + } + else + { + information[1] = NULL; + } + updateSaveListFinished = true; - return tempSaveList; + return information; } void SearchModel::UpdateSaveList(int pageNumber, std::string query) @@ -46,6 +65,11 @@ void SearchModel::UpdateSaveList(int pageNumber, std::string query) selected.clear(); notifySelectedChanged(); + if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == "best" && query == "") + SetShowTags(true); + else + SetShowTags(false); + //Threading if(!updateSaveListWorking) { @@ -78,6 +102,11 @@ vector SearchModel::GetSaveList() return saveList; } +vector > SearchModel::GetTagList() +{ + return tagList; +} + void SearchModel::Update() { if(updateSaveListWorking) @@ -87,10 +116,25 @@ void SearchModel::Update() updateSaveListWorking = false; lastError = ""; saveListLoaded = true; - vector * tempSaveList; - pthread_join(updateSaveListThread, (void**)(&tempSaveList)); - saveList = *tempSaveList; - delete tempSaveList; + void ** tempInformation; + //vector * tempSaveList; + pthread_join(updateSaveListThread, (void**)(&tempInformation)); + saveList = *(vector*)tempInformation[0]; + + delete (vector*)tempInformation[0]; + + if(tempInformation[1]) + { + tagList = *(vector >*)tempInformation[1]; + delete (vector >*)tempInformation[1]; + } + else + { + tagList = vector >(); + } + + delete[] tempInformation; + if(!saveList.size()) { lastError = Client::Ref().GetLastError(); diff --git a/src/search/SearchModel.h b/src/search/SearchModel.h index 831e14146..385ff0576 100644 --- a/src/search/SearchModel.h +++ b/src/search/SearchModel.h @@ -21,10 +21,12 @@ private: vector selected; vector observers; vector saveList; + vector > tagList; int currentPage; int resultCount; bool showOwn; bool showFavourite; + bool showTags; void notifySaveListChanged(); void notifySelectedChanged(); void notifyPageChanged(); @@ -43,9 +45,11 @@ public: SearchModel(); virtual ~SearchModel(); + void SetShowTags(bool show); void AddObserver(SearchView * observer); void UpdateSaveList(int pageNumber, std::string query); vector GetSaveList(); + vector > GetTagList(); string GetLastError() { return lastError; } int GetPageCount() { return max(1, (int)(ceil(resultCount/16))); } int GetPageNum() { return currentPage; } diff --git a/src/search/SearchView.cpp b/src/search/SearchView.cpp index 16d03ed3b..cf029858d 100644 --- a/src/search/SearchView.cpp +++ b/src/search/SearchView.cpp @@ -16,6 +16,7 @@ SearchView::SearchView(): nextButton = new ui::Button(ui::Point(XRES+BARSIZE-52, YRES+MENUSIZE-18), ui::Point(50, 16), "Next \x95"); previousButton = new ui::Button(ui::Point(1, YRES+MENUSIZE-18), ui::Point(50, 16), "\x96 Prev"); infoLabel = new ui::Label(ui::Point(51, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-102, 16), "Loading..."); + tagsLabel = new ui::Label(ui::Point(51, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-102, 16), "Popular Tags:"); class SearchAction : public ui::TextboxAction { @@ -219,6 +220,12 @@ SearchView::~SearchView() delete infoLabel; } +void SearchView::Search(std::string query) +{ + searchField->SetText(query); + c->DoSearch(query, true); +} + void SearchView::NotifySortChanged(SearchModel * sender) { sortButton->SetText("Show "+sender->GetSort()); @@ -293,14 +300,29 @@ void SearchView::NotifySaveListChanged(SearchModel * sender) int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1; int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; + int tagWidth, tagHeight, tagX = 0, tagY = 0, tagsX = 6, tagsY = 4, tagPadding = 1; + int tagAreaWidth, tagAreaHeight, tagXOffset, tagYOffset; + vector saves = sender->GetSaveList(); + vector > tags = sender->GetTagList(); + //string messageOfTheDay = sender->GetMessageOfTheDay(); + + RemoveComponent(tagsLabel); + tagsLabel->SetParentWindow(NULL); + Client::Ref().ClearThumbnailRequests(); for(i = 0; i < saveButtons.size(); i++) { RemoveComponent(saveButtons[i]); delete saveButtons[i]; } + for(i = 0; i < tagButtons.size(); i++) + { + RemoveComponent(tagButtons[i]); + delete tagButtons[i]; + } saveButtons.clear(); + tagButtons.clear(); if(!sender->GetSavesLoaded()) { nextButton->Enabled = false; @@ -341,12 +363,34 @@ void SearchView::NotifySaveListChanged(SearchModel * sender) delete errorLabel; errorLabel = NULL; } - buttonXOffset = buttonPadding; + buttonYOffset = 28; + buttonXOffset = buttonPadding; buttonAreaWidth = Size.X; buttonAreaHeight = Size.Y - buttonYOffset - 18; + + if(tags.size()) + { + buttonYOffset += (buttonAreaHeight/savesY) - buttonPadding*2; + buttonAreaHeight = Size.Y - buttonYOffset - 18; + savesY--; + + tagXOffset = tagPadding; + tagYOffset = 60; + tagAreaWidth = Size.X; + tagAreaHeight = ((buttonAreaHeight/savesY) - buttonPadding*2)-(tagYOffset-28)-5; + tagWidth = (tagAreaWidth/tagsX) - tagPadding*2; + tagHeight = (tagAreaHeight/tagsY) - tagPadding*2; + + AddComponent(tagsLabel); + tagsLabel->Position.Y = tagYOffset-16; + } + buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2; buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2; + + + class SaveOpenAction: public ui::SaveButtonAction { SearchView * v; @@ -361,6 +405,59 @@ void SearchView::NotifySaveListChanged(SearchModel * sender) v->c->Selected(sender->GetSave()->GetID(), sender->GetSelected()); } }; + + class TagAction: public ui::ButtonAction + { + SearchView * v; + std::string tag; + public: + TagAction(SearchView * v, std::string tag) : v(v), tag(tag) {} + virtual void ActionCallback(ui::Button * sender) + { + v->Search(tag); + } + }; + + for(i = 0; i < tags.size(); i++) + { + int maxTagVotes = tags[0].second; + + pair tag = tags[i]; + + if(tagX == tagsX) + { + if(tagY == tagsY-1) + break; + tagX = 0; + tagY++; + } + + int tagAlpha = 192; + if (maxTagVotes) + tagAlpha = 127+(128*tag.second)/maxTagVotes; + + ui::Button * tagButton; + tagButton = new ui::Button( + ui::Point( + tagXOffset + tagPadding + tagX*(tagWidth+tagPadding*2), + tagYOffset + tagPadding + tagY*(tagHeight+tagPadding*2) + ), + ui::Point(tagWidth, tagHeight), + tag.first + ); + tagButton->SetActionCallback(new TagAction(this, tag.first)); + tagButton->Appearance.BorderInactive = ui::Colour(0, 0, 0); + tagButton->Appearance.BorderHover = ui::Colour(0, 0, 0); + tagButton->Appearance.BorderActive = ui::Colour(0, 0, 0); + tagButton->Appearance.BackgroundHover = ui::Colour(0, 0, 0); + + tagButton->Appearance.TextInactive = ui::Colour(tagAlpha, tagAlpha, tagAlpha); + tagButton->Appearance.TextHover = ui::Colour((tagAlpha*5)/6, (tagAlpha*5)/6, tagAlpha); + AddComponent(tagButton); + tagButtons.push_back(tagButton); + tagX++; + + } for(i = 0; i < saves.size(); i++) { if(saveX == savesX) diff --git a/src/search/SearchView.h b/src/search/SearchView.h index 6e27f0615..abded8591 100644 --- a/src/search/SearchView.h +++ b/src/search/SearchView.h @@ -19,12 +19,14 @@ class SearchView: public ui::Window private: SearchController * c; vector saveButtons; + vector tagButtons; ui::Button * favButton; ui::Button * nextButton; ui::Button * previousButton; ui::Label * errorLabel; ui::Textbox * searchField; ui::Label * infoLabel; + ui::Label * tagsLabel; ui::Button * sortButton; ui::Button * ownButton; ui::Spinner * loadingSpinner; @@ -44,6 +46,7 @@ public: SearchView(); virtual ~SearchView(); void AttachController(SearchController * _c) { c = _c; } + virtual void Search(std::string); virtual void OnTick(float dt); virtual void OnMouseWheel(int x, int y, int d); virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);