aboutsummaryrefslogtreecommitdiff
path: root/src/QuickMedia.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r--src/QuickMedia.cpp266
1 files changed, 197 insertions, 69 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 9adbc22..44e6bd9 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -35,6 +35,8 @@ static const int DOUBLE_CLICK_TIME = 500;
static const std::string fourchan_google_captcha_api_key = "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc";
static const float tab_text_size = 18.0f;
static const float tab_height = tab_text_size + 10.0f;
+static const sf::Color tab_selected_color(0, 85, 119);
+static const sf::Color tab_unselected_color(43, 45, 47);
// Prevent writing to broken pipe from exiting the program
static void sigpipe_handler(int unused) {
@@ -756,6 +758,56 @@ namespace QuickMedia {
sf::Text *text;
};
+ bool Program::on_search_suggestion_submit_text(Body *input_body, Body *output_body) {
+ if(input_body->no_items_visible())
+ return false;
+
+ Page next_page = current_plugin->get_page_after_search();
+ bool skip_search = next_page == Page::VIDEO_CONTENT;
+ // TODO: This shouldn't be done if search_selected_suggestion fails
+ if(search_selected_suggestion(input_body, output_body, current_plugin, content_title, content_url, skip_search) != SearchResult::OK) {
+ show_notification("Search", "Search failed!", Urgency::CRITICAL);
+ return false;
+ }
+
+ if(next_page == Page::EPISODE_LIST && current_plugin->is_manga()) {
+ Manga *manga_plugin = static_cast<Manga*>(current_plugin);
+ if(content_url.empty()) {
+ show_notification("Manga", "Url is missing for manga!", Urgency::CRITICAL);
+ return false;
+ }
+
+ Path content_storage_dir = get_storage_dir().join(current_plugin->name);
+
+ std::string manga_id;
+ if(!manga_plugin->extract_id_from_url(content_url, manga_id))
+ return false;
+
+ manga_id_base64 = base64_encode(manga_id);
+ content_storage_file = content_storage_dir.join(manga_id_base64);
+ content_storage_json.clear();
+ content_storage_json["name"] = content_title;
+ FileType file_type = get_file_type(content_storage_file);
+ if(file_type == FileType::REGULAR)
+ read_file_as_json(content_storage_file, content_storage_json);
+ } else if(next_page == Page::VIDEO_CONTENT) {
+ watched_videos.clear();
+ if(content_url.empty())
+ next_page = Page::SEARCH_RESULT;
+ else {
+ page_stack.push(Page::SEARCH_SUGGESTION);
+ }
+ current_page = next_page;
+ return false;
+ } else if(next_page == Page::CONTENT_LIST) {
+ content_list_url = content_url;
+ } else if(next_page == Page::IMAGE_BOARD_THREAD_LIST) {
+ image_board_thread_list_url = content_url;
+ }
+ current_page = next_page;
+ return true;
+ }
+
void Program::search_suggestion_page() {
std::string update_search_text;
bool search_running = false;
@@ -855,51 +907,7 @@ namespace QuickMedia {
if(typing || tabs[selected_tab].body->no_items_visible())
return false;
}
-
- Page next_page = current_plugin->get_page_after_search();
- bool skip_search = next_page == Page::VIDEO_CONTENT;
- // TODO: This shouldn't be done if search_selected_suggestion fails
- if(search_selected_suggestion(tabs[selected_tab].body, body, current_plugin, content_title, content_url, skip_search) != SearchResult::OK) {
- show_notification("Search", "Search failed!", Urgency::CRITICAL);
- return false;
- }
-
- if(next_page == Page::EPISODE_LIST && current_plugin->is_manga()) {
- Manga *manga_plugin = static_cast<Manga*>(current_plugin);
- if(content_url.empty()) {
- show_notification("Manga", "Url is missing for manga!", Urgency::CRITICAL);
- return false;
- }
-
- Path content_storage_dir = get_storage_dir().join(current_plugin->name);
-
- std::string manga_id;
- if(!manga_plugin->extract_id_from_url(content_url, manga_id))
- return false;
-
- manga_id_base64 = base64_encode(manga_id);
- content_storage_file = content_storage_dir.join(manga_id_base64);
- content_storage_json.clear();
- content_storage_json["name"] = content_title;
- FileType file_type = get_file_type(content_storage_file);
- if(file_type == FileType::REGULAR)
- read_file_as_json(content_storage_file, content_storage_json);
- } else if(next_page == Page::VIDEO_CONTENT) {
- watched_videos.clear();
- if(content_url.empty())
- next_page = Page::SEARCH_RESULT;
- else {
- page_stack.push(Page::SEARCH_SUGGESTION);
- }
- current_page = next_page;
- return false;
- } else if(next_page == Page::CONTENT_LIST) {
- content_list_url = content_url;
- } else if(next_page == Page::IMAGE_BOARD_THREAD_LIST) {
- image_board_thread_list_url = content_url;
- }
- current_page = next_page;
- return true;
+ return on_search_suggestion_submit_text(tabs[selected_tab].body, body);
};
std::future<BodyItems> recommended_future;
@@ -921,8 +929,6 @@ namespace QuickMedia {
bool redraw = true;
sf::Event event;
- const sf::Color tab_selected_color(0, 85, 119);
- const sf::Color tab_unselected_color(43, 45, 47);
sf::RectangleShape tab_spacing_rect(sf::Vector2f(0.0f, 0.0f));
tab_spacing_rect.setFillColor(tab_unselected_color);
const float tab_spacer_height = 0.0f;
@@ -1617,41 +1623,120 @@ namespace QuickMedia {
return Page::EXIT;
}
+ enum class EpisodeListTabType {
+ CHAPTERS,
+ CREATOR
+ };
+
+ struct EpisodeListTab {
+ EpisodeListTabType type;
+ Body *body;
+ const Creator *creator;
+ std::future<BodyItems> creator_page_download_future;
+ sf::Text text;
+ };
+
void Program::episode_list_page() {
- search_bar->onTextUpdateCallback = [this](const std::string &text) {
- body->filter_search_fuzzy(text);
- body->select_first_item();
+ assert(current_plugin->is_manga());
+ Manga *manga = static_cast<Manga*>(current_plugin);
+
+ Json::Value *json_chapters = &content_storage_json["chapters"];
+ std::vector<EpisodeListTab> tabs;
+ int selected_tab = 0;
+
+ search_bar->onTextUpdateCallback = [&tabs, &selected_tab](const std::string &text) {
+ tabs[selected_tab].body->filter_search_fuzzy(text);
+ tabs[selected_tab].body->clamp_selection();
};
- search_bar->onTextSubmitCallback = [this](const std::string &text) -> bool {
- BodyItem *selected_item = body->get_selected();
- if(!selected_item)
- return false;
+ search_bar->onTextSubmitCallback = [this, &tabs, &selected_tab, &json_chapters](const std::string &text) -> bool {
+ if(tabs[selected_tab].type == EpisodeListTabType::CHAPTERS) {
+ BodyItem *selected_item = body->get_selected();
+ if(!selected_item)
+ return false;
- select_episode(selected_item, false);
- return true;
+ select_episode(selected_item, false);
+ return true;
+ } else {
+ if(on_search_suggestion_submit_text(tabs[selected_tab].body, body)) {
+ selected_tab = 0;
+ json_chapters = &content_storage_json["chapters"];
+ return true;
+ } else {
+ return false;
+ }
+ }
};
- const Json::Value &json_chapters = content_storage_json["chapters"];
+ auto download_create_page = [manga](std::string url) {
+ BodyItems body_items;
+ if(manga->get_creators_manga_list(url, body_items) != PluginResult::OK)
+ show_notification("Manga", "Failed to download authors page", Urgency::CRITICAL);
+ return body_items;
+ };
+ EpisodeListTab chapters_tab;
+ chapters_tab.type = EpisodeListTabType::CHAPTERS;
+ chapters_tab.body = body;
+ chapters_tab.creator = nullptr;
+ chapters_tab.text = sf::Text("Chapters", font, tab_text_size);
+ tabs.push_back(std::move(chapters_tab));
+
+ const std::vector<Creator>& creators = manga->get_creators();
+ for(const Creator &creator : creators) {
+ EpisodeListTab tab;
+ tab.type = EpisodeListTabType::CREATOR;
+ tab.body = new Body(this, &font, &bold_font);
+ tab.body->draw_thumbnails = true;
+ tab.creator = &creator;
+ tab.creator_page_download_future = std::async(std::launch::async, download_create_page, creator.url);
+ tab.text = sf::Text(creator.name, font, tab_text_size);
+ tabs.push_back(std::move(tab));
+ }
+
+ const float tab_spacer_height = 0.0f;
sf::Vector2f body_pos;
sf::Vector2f body_size;
bool redraw = true;
sf::Event event;
+ sf::RectangleShape tab_drop_shadow;
+ tab_drop_shadow.setFillColor(sf::Color(23, 25, 27));
+
while (current_page == Page::EPISODE_LIST) {
while (window.pollEvent(event)) {
- base_event_handler(event, Page::SEARCH_SUGGESTION);
+ base_event_handler(event, Page::SEARCH_SUGGESTION, false, true);
if(event.type == sf::Event::Resized || event.type == sf::Event::GainedFocus)
redraw = true;
- else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::T && sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) {
- BodyItem *selected_item = body->get_selected();
- if(selected_item) {
- if(track_media(TrackMediaType::HTML, content_title, selected_item->get_title(), content_url) == 0) {
- show_notification("Media tracker", "You are now tracking \"" + content_title + "\" after \"" + selected_item->get_title() + "\"", Urgency::LOW);
- } else {
- show_notification("Media tracker", "Failed to track media \"" + content_title + "\", chapter: \"" + selected_item->get_title() + "\"", Urgency::CRITICAL);
+ else if(event.type == sf::Event::KeyPressed) {
+ if(event.key.code == sf::Keyboard::T && event.key.control && tabs[selected_tab].type == EpisodeListTabType::CHAPTERS) {
+ BodyItem *selected_item = body->get_selected();
+ if(selected_item) {
+ if(track_media(TrackMediaType::HTML, content_title, selected_item->get_title(), content_url) == 0) {
+ show_notification("Media tracker", "You are now tracking \"" + content_title + "\" after \"" + selected_item->get_title() + "\"", Urgency::LOW);
+ } else {
+ show_notification("Media tracker", "Failed to track media \"" + content_title + "\", chapter: \"" + selected_item->get_title() + "\"", Urgency::CRITICAL);
+ }
}
+ } else if(event.key.code == sf::Keyboard::Up) {
+ tabs[selected_tab].body->select_previous_item();
+ } else if(event.key.code == sf::Keyboard::Down) {
+ tabs[selected_tab].body->select_next_item();
+ } else if(event.key.code == sf::Keyboard::Escape) {
+ current_page = Page::SEARCH_SUGGESTION;
+ body->clear_items();
+ body->reset_selected();
+ search_bar->clear();
+ } else if(event.key.code == sf::Keyboard::Left) {
+ tabs[selected_tab].body->filter_search_fuzzy("");
+ tabs[selected_tab].body->clamp_selection();
+ selected_tab = std::max(0, selected_tab - 1);
+ search_bar->clear();
+ } else if(event.key.code == sf::Keyboard::Right) {
+ tabs[selected_tab].body->filter_search_fuzzy("");
+ tabs[selected_tab].body->clamp_selection();
+ selected_tab = std::min((int)tabs.size() - 1, selected_tab + 1);
+ search_bar->clear();
}
}
}
@@ -1660,16 +1745,59 @@ namespace QuickMedia {
if(redraw) {
redraw = false;
search_bar->onWindowResize(window_size);
- get_body_dimensions(window_size, search_bar.get(), body_pos, body_size);
+ get_body_dimensions(window_size, search_bar.get(), body_pos, body_size, true);
}
search_bar->update();
window.clear(back_color);
- body->draw(window, body_pos, body_size, json_chapters);
- search_bar->draw(window);
+
+ const float width_per_tab = window_size.x / tabs.size();
+ sf::RectangleShape tab_background(sf::Vector2f(std::floor(width_per_tab), tab_height));
+
+ float tab_vertical_offset = search_bar->getBottomWithoutShadow();
+ if(tabs[selected_tab].type == EpisodeListTabType::CHAPTERS)
+ tabs[selected_tab].body->draw(window, body_pos, body_size, *json_chapters);
+ else
+ tabs[selected_tab].body->draw(window, body_pos, body_size);
+ const float tab_y = tab_spacer_height + std::floor(tab_vertical_offset + tab_height * 0.5f - (tab_text_size + 5.0f) * 0.5f);
+
+ int i = 0;
+ for(EpisodeListTab &tab : tabs) {
+ if(tab.type == EpisodeListTabType::CREATOR
+ && tab.creator_page_download_future.valid()
+ && tab.creator_page_download_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ {
+ tab.body->items = tab.creator_page_download_future.get();
+ tab.body->filter_search_fuzzy(search_bar->get_text());
+ tab.body->clamp_selection();
+ }
+
+ if(i == selected_tab)
+ tab_background.setFillColor(tab_selected_color);
+ else
+ tab_background.setFillColor(tab_unselected_color);
+
+ tab_background.setPosition(std::floor(i * width_per_tab), tab_spacer_height + std::floor(tab_vertical_offset));
+ window.draw(tab_background);
+ const float center = (i * width_per_tab) + (width_per_tab * 0.5f);
+ tab.text.setPosition(std::floor(center - tab.text.getLocalBounds().width * 0.5f), tab_y);
+ window.draw(tab.text);
+ ++i;
+ }
+
+ tab_drop_shadow.setSize(sf::Vector2f(window_size.x, 5.0f));
+ tab_drop_shadow.setPosition(0.0f, std::floor(tab_vertical_offset + tab_height));
+ window.draw(tab_drop_shadow);
+
+ search_bar->draw(window, false);
window.display();
}
+
+ for(EpisodeListTab &tab : tabs) {
+ if(tab.type == EpisodeListTabType::CREATOR)
+ delete tab.body;
+ }
}
// TODO: Optimize this somehow. One image alone uses more than 20mb ram! Total ram usage for viewing one image
@@ -2466,7 +2594,7 @@ namespace QuickMedia {
body->items[reply_index]->visible = true;
}
}
- } else if(event.key.code == sf::Keyboard::C && sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) && selected_item) {
+ } else if(event.key.code == sf::Keyboard::C && event.key.control && selected_item) {
navigation_stage = NavigationStage::REPLYING;
} else if(event.key.code == sf::Keyboard::R && selected_item) {
std::string text_to_add = ">>" + selected_item->post_number;