diff options
author | dec05eba <dec05eba@protonmail.com> | 2020-09-10 20:40:17 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2020-09-10 20:40:17 +0200 |
commit | 6f11b42b7015646bcdc5f8bbec7da61e76655861 (patch) | |
tree | 364f9bbe02a9f8806a2640f36ebbf8a1da70fdb8 | |
parent | 901edef180648ce9c76ec4f73005da6498f57cf0 (diff) |
Add ctrl+r keybind to show related videos menu
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | include/Body.hpp | 5 | ||||
-rw-r--r-- | include/QuickMedia.hpp | 2 | ||||
-rw-r--r-- | src/Body.cpp | 27 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 113 | ||||
-rw-r--r-- | src/Text.cpp | 16 |
6 files changed, 131 insertions, 40 deletions
@@ -27,7 +27,8 @@ If you are running arch linux then you can install QuickMedia from aur (https:// Press `arrow up` and `arrow down` to navigate the menu and also to scroll to the previous/next image when viewing manga in scorll mode. Alternatively you can use the mouse scroll to scroll to the previous/next manga in scroll mode.\ Press `Enter` (aka `Return`) to select the item.\ Press `ESC` to go back to the previous menu.\ -Press `Ctrl + F` to toggle fullscreen when watching a video.\ +Press `Ctrl + F` to switch between window mode and fullscreen mode when watching a video.\ +Press `Ctrl + R` to show/hide related videos menu when watching a video.\ Press `Ctrl + T` when hovering over a manga chapter to start tracking manga after that chapter. This only works if AutoMedia is installed and accessible in PATH environment variable.\ Press `Backspace` to return to the preview item when reading replies in image board threads.\ @@ -64,8 +65,6 @@ See project.conf \[dependencies]. # TODO If a search returns no results, then "No results found for ..." should be shown and navigation should go back to searching with suggestions.\ Give user the option to start where they left off or from the start or from the start.\ -Search is asynchronous, but download of image also needs to be asynchronous, also add loading animation.\ -Retain search text when navigating back.\ Disable ytdl_hook subtitles. If a video has subtitles for many languages, then it will stall video playback for several seconds until all subtitles have been downloaded and loaded. Add grid-view when thumbnails are visible.\ @@ -78,12 +77,9 @@ then start downloading from the current page instead of page 1.\ Show progress of manga in the history tab (current chapter out of total chapters).\ Animate page navigation.\ Properly format text in items. For example for 4chan. The size of the item should also change.\ -Wrap text that is too long.\ Add greentext support for quotes.\ Add support for special formatting for posts by admins on imageboards.\ For image boards, track (You)'s and show notification when somebody replies to your post.\ -In image boards when viewing videos, automatically play the next one after the current one has finished playing (just like the youtube plugin does).\ -When viewing images the image download start from page 1 to the end page. The image download should start from the viewing page.\ Go to next chapter when reaching the end of the chapter in image endless mode.\ Some text is not visible on 4chan, such as code blocks.\ Show indication when search is in progress (or when something is loading). Some sites such as youtube and mangadex are slow at searching.\ diff --git a/include/Body.hpp b/include/Body.hpp index 86b6984..a3058a6 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -55,7 +55,7 @@ namespace QuickMedia { class Body { public: - Body(Program *program, sf::Font &font, sf::Font &bold_font); + Body(Program *program, sf::Font *font, sf::Font *bold_font); // Select previous item, ignoring invisible items void select_previous_item(); @@ -82,7 +82,8 @@ namespace QuickMedia { bool no_items_visible() const; - sf::Text title_text; + sf::Font *font; + sf::Font *bold_font; sf::Text progress_text; sf::Text author_text; sf::Text replies_text; diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index e4855f9..27863cc 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -99,6 +99,6 @@ namespace QuickMedia { bool use_system_mpv_config = false; // TODO: Save this to config file when switching modes ImageViewMode image_view_mode = ImageViewMode::SINGLE; - BodyItems related_media; + Body *related_media_body; }; }
\ No newline at end of file diff --git a/src/Body.cpp b/src/Body.cpp index 6081028..61d100b 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -10,17 +10,17 @@ const sf::Color front_color(43, 45, 47); const sf::Color back_color(33, 35, 37); namespace QuickMedia { - Body::Body(Program *program, sf::Font &font, sf::Font &bold_font) : + Body::Body(Program *program, sf::Font *font, sf::Font *bold_font) : program(program), - title_text("", font, 14), - progress_text("", font, 14), - author_text("", bold_font, 14), - replies_text("", font, 14), + font(font), + bold_font(bold_font), + progress_text("", *font, 14), + author_text("", *bold_font, 14), + replies_text("", *font, 14), selected_item(0), draw_thumbnails(false), loading_thumbnail(false) { - title_text.setFillColor(sf::Color::White); progress_text.setFillColor(sf::Color::White); author_text.setFillColor(sf::Color::White); replies_text.setFillColor(sf::Color(129, 162, 190)); @@ -129,7 +129,6 @@ namespace QuickMedia { } // TODO: Use a render target for the whole body so all images can be put into one. - // TODO: Unload thumbnails once they are no longer visible on the screen. // TODO: Load thumbnails with more than one thread. // TODO: Show chapters (rows) that have been read differently to make it easier to see what hasn't been read yet. void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress) { @@ -163,18 +162,14 @@ namespace QuickMedia { thumbnail_it.second.referenced = false; } - // TODO: Instead of generating a new hash everytime to access textures, cache the hash of the thumbnail url for(auto &body_item : items) { - // Intentionally create the item with the key item->thumbnail_url if it doesn't exist - item_thumbnail_textures[body_item->thumbnail_url].referenced = true; - if(body_item->dirty) { body_item->dirty = false; if(body_item->title_text) body_item->title_text->setString(body_item->title); else - body_item->title_text = std::make_unique<Text>(body_item->title, title_text.getFont(), 14, size.x - 50 - image_padding_x * 2.0f); - //body_item->title_text->updateGeometry(); + body_item->title_text = std::make_unique<Text>(body_item->title, font, 14, size.x - 50 - image_padding_x * 2.0f); + //body_item->title_text->updateGeometry(); // TODO: Call this to make getHeight work on first frame (called below) } } @@ -211,7 +206,6 @@ namespace QuickMedia { for(int i = first_visible_item + 1; i < num_items; ++i) { const auto &item = items[i]; - auto &item_thumbnail = item_thumbnail_textures[item->thumbnail_url]; if(pos.y >= start_y + size.y) break; @@ -219,6 +213,11 @@ namespace QuickMedia { if(!item->visible) continue; + // TODO: Instead of generating a new hash everytime to access textures, cache the hash of the thumbnail url + // Intentionally create the item with the key item->thumbnail_url if it doesn't exist + item_thumbnail_textures[item->thumbnail_url].referenced = true; + auto &item_thumbnail = item_thumbnail_textures[item->thumbnail_url]; + float item_height = item->title_text->getHeight(); if(!item->author.empty()) { item_height += author_text.getCharacterSize() + 2.0f; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 27926cb..37264d7 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -74,7 +74,9 @@ namespace QuickMedia { fprintf(stderr, "Failed to load font: Lato-Bold.ttf\n"); abort(); } - body = new Body(this, font, bold_font); + body = new Body(this, &font, &bold_font); + related_media_body = new Body(this, &font, &bold_font); + related_media_body->draw_thumbnails = true; struct sigaction action; action.sa_handler = sigpipe_handler; @@ -87,6 +89,7 @@ namespace QuickMedia { } Program::~Program() { + delete related_media_body; delete body; delete current_plugin; if(disp) @@ -584,6 +587,21 @@ namespace QuickMedia { body_size = sf::Vector2f(body_width, window_size.y - search_bottom - body_padding_vertical - tab_h); } + static const float RELATED_MEDIA_WINDOW_WIDTH = 1.0f; + static void get_related_media_body_dimensions(const sf::Vector2f &window_size, sf::Vector2f &body_pos, sf::Vector2f &body_size, float related_videos_text_height) { + float body_padding_horizontal = 25.0f; + float body_padding_vertical = 25.0f; + float body_width = (window_size.x * RELATED_MEDIA_WINDOW_WIDTH) - body_padding_horizontal * 2.0f; + if(body_width <= 480.0f) { + body_width = (window_size.x * RELATED_MEDIA_WINDOW_WIDTH); + body_padding_horizontal = 0.0f; + body_padding_vertical = 10.0f; + } + + body_pos = sf::Vector2f(body_padding_horizontal, body_padding_vertical + related_videos_text_height); + body_size = sf::Vector2f(body_width, window_size.y - body_padding_vertical - related_videos_text_height); + } + void Program::search_suggestion_page() { std::string update_search_text; bool search_running = false; @@ -592,14 +610,14 @@ namespace QuickMedia { std::string autocomplete_text; bool autocomplete_running = false; - Body history_body(this, font, bold_font); + Body history_body(this, &font, &bold_font); std::unique_ptr<Body> recommended_body; sf::Text all_tab_text("All", font, tab_text_size); sf::Text history_tab_text("History", font, tab_text_size); sf::Text recommended_tab_text("Recommended", font, tab_text_size); if(current_plugin->name == "youtube") { - recommended_body = std::make_unique<Body>(this, font, bold_font); + recommended_body = std::make_unique<Body>(this, &font, &bold_font); recommended_body->draw_thumbnails = true; fill_recommended_items_from_json(load_recommended_json(current_plugin), recommended_body->items); } @@ -993,7 +1011,7 @@ namespace QuickMedia { } int saved_recommendation_count = 0; - for(const auto &body_item : related_media) { + for(const auto &body_item : related_media_body->items) { std::string recommended_video_id; if(youtube_url_extract_id(body_item->url, recommended_video_id)) { Json::Value &existing_recommendation = recommended_json[recommended_video_id]; @@ -1036,10 +1054,26 @@ namespace QuickMedia { Page previous_page = pop_page_stack(); std::unique_ptr<VideoPlayer> video_player; + std::unique_ptr<sf::RenderWindow> related_media_window; + sf::Vector2f related_media_window_size; + bool related_media_window_visible = false; + + sf::Text related_videos_text("Related videos", bold_font, 20); + const float related_videos_text_height = related_videos_text.getCharacterSize(); sf::WindowHandle video_player_window = None; - auto on_window_create = [this, &video_player_window](sf::WindowHandle _video_player_window) mutable { + auto on_window_create = [this, &video_player_window, &related_media_window, &related_media_window_size](sf::WindowHandle _video_player_window) mutable { video_player_window = _video_player_window; + + if(!current_plugin->is_image_board()) { + related_media_window_size.x = window_size.x * RELATED_MEDIA_WINDOW_WIDTH; + related_media_window_size.y = window_size.y; + related_media_window = std::make_unique<sf::RenderWindow>(sf::VideoMode(related_media_window_size.x, related_media_window_size.y), "", 0); + related_media_window->setVerticalSyncEnabled(true); + related_media_window->setVisible(false); + XReparentWindow(disp, related_media_window->getSystemHandle(), video_player_window, window_size.x - related_media_window_size.x, 0); + } + XSelectInput(disp, video_player_window, KeyPressMask | PointerMotionMask); XSync(disp, False); }; @@ -1055,7 +1089,8 @@ namespace QuickMedia { show_notification("Video player", err_msg.c_str(), Urgency::CRITICAL); current_page = previous_page; } else { - related_media = current_plugin->get_related_media(content_url); + related_media_body->clear_items(); + related_media_body->items = current_plugin->get_related_media(content_url); // TODO: Make this also work for other video plugins if(current_plugin->name != "youtube") @@ -1116,7 +1151,7 @@ namespace QuickMedia { std::string new_video_title; // Find video that hasn't been played before in this video session // TODO: Remove duplicates - for(auto it = related_media.begin(), end = related_media.end(); it != end; ++it) { + for(auto it = related_media_body->items.begin(), end = related_media_body->items.end(); it != end; ++it) { if(watched_videos.find((*it)->url) == watched_videos.end()) { new_video_url = (*it)->url; new_video_title = (*it)->title; @@ -1158,14 +1193,61 @@ namespace QuickMedia { while (current_page == Page::VIDEO_CONTENT) { while (window.pollEvent(event)) { base_event_handler(event, previous_page, true, false, false); + if(event.type == sf::Event::Resized && related_media_window) { + related_media_window_size.x = window_size.x * RELATED_MEDIA_WINDOW_WIDTH; + related_media_window_size.y = window_size.y; + related_media_window->setSize(sf::Vector2u(related_media_window_size.x, related_media_window_size.y)); + related_media_window->setPosition(sf::Vector2i(window_size.x - related_media_window_size.x, 0)); + sf::FloatRect visible_area(0, 0, related_media_window_size.x, related_media_window_size.y); + related_media_window->setView(sf::View(visible_area)); + } + } + + while(related_media_window && related_media_window_visible && related_media_window->pollEvent(event)) { + if(event.type == sf::Event::KeyPressed) { + if(event.key.code == sf::Keyboard::Up) { + related_media_body->select_previous_item(); + } else if(event.key.code == sf::Keyboard::Down) { + related_media_body->select_next_item(); + } else if(event.key.code == sf::Keyboard::Escape) { + related_media_window_visible = false; + related_media_window->setVisible(false); + } else if(event.key.code == sf::Keyboard::R && event.key.control) { + related_media_window_visible = false; + related_media_window->setVisible(related_media_window_visible); + } else if(event.key.code == sf::Keyboard::Enter) { + BodyItem *selected_item = related_media_body->get_selected(); + if(!selected_item) + continue; + + related_media_window.reset(); + related_media_window_visible = false; + + video_player_window = None; + has_video_started = true; + video_player.reset(); + video_player = std::make_unique<VideoPlayer>(current_plugin->use_tor, use_system_mpv_config, video_event_callback, on_window_create); + + content_url = selected_item->url; + content_title = selected_item->title; + load_video_error_check(); + } + } } if(video_player_window && XCheckTypedWindowEvent(disp, video_player_window, KeyPress, &xev)/* && xev.xkey.subwindow == video_player_window*/) { KeySym pressed_keysym = XKeycodeToKeysym(disp, xev.xkey.keycode, 0); + bool pressing_ctrl = (CLEANMASK(xev.xkey.state) == ControlMask); if(pressed_keysym == XK_Escape) { current_page = previous_page; - } else if(pressed_keysym == XK_f && CLEANMASK(xev.xkey.state) == ControlMask) { + } else if(pressed_keysym == XK_f && pressing_ctrl) { window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE); + } else if(pressed_keysym == XK_r && pressing_ctrl && related_media_window && !related_media_window_visible) { + related_media_window_visible = true; + related_media_window->setVisible(related_media_window_visible); + if(!cursor_visible) + window.setMouseCursorVisible(true); + cursor_visible = true; } } @@ -1199,6 +1281,18 @@ namespace QuickMedia { } if(video_player_window) { + if(related_media_window && related_media_window_visible) { + sf::Vector2f body_pos, body_size; + get_related_media_body_dimensions(window_size, body_pos, body_size, related_videos_text_height); + + related_media_window->clear(back_color); + related_videos_text.setPosition(body_pos.x, 10.0f); + related_media_window->draw(related_videos_text); + related_media_body->draw(*related_media_window, body_pos, body_size); + related_media_window->display(); + continue; + } + if(!cursor_visible) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); continue; @@ -1209,8 +1303,9 @@ namespace QuickMedia { cursor_visible = false; window.setMouseCursorVisible(false); } + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); } window.setMouseCursorVisible(true); diff --git a/src/Text.cpp b/src/Text.cpp index da21b1d..9ec2f68 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -181,6 +181,14 @@ namespace QuickMedia // Logic loosely based on https://github.com/SFML/SFML/wiki/Source:-CurvedText void Text::updateGeometry(bool update_even_if_not_dirty) { + if(dirtyText) + { + textElements.clear(); + StringViewUtf32 wholeStr(this->str.getData(), this->str.getSize()); + textElements.push_back({ wholeStr, TextElement::Type::TEXT }); + dirtyText = false; + } + if(!update_even_if_not_dirty && !dirty) return; @@ -599,14 +607,6 @@ namespace QuickMedia bool Text::draw(sf::RenderTarget &target) { - if(dirtyText) - { - textElements.clear(); - StringViewUtf32 wholeStr(this->str.getData(), this->str.getSize()); - textElements.push_back({ wholeStr, TextElement::Type::TEXT }); - dirtyText = false; - } - updateGeometry(); if(dirtyCaret || caretMoveDirection != CaretMoveDirection::NONE) |