From c207201a4a5a47e4ad286ba7371e4176c8ed5056 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 19 Mar 2021 12:22:25 +0100 Subject: Start on touch support, add support for ui scaling, revert back to old get_message_by_id method for matrix to support loading user display names in replies --- README.md | 4 ++ TODO | 1 - include/Body.hpp | 25 +++++--- include/QuickMedia.hpp | 1 + include/Utils.hpp | 5 ++ src/Body.cpp | 164 ++++++++++++++++++++++++++++++++++++++++--------- src/Entry.cpp | 15 ++--- src/QuickMedia.cpp | 103 +++++++++++++++++++------------ src/SearchBar.cpp | 21 ++++--- src/Text.cpp | 23 +++---- src/Utils.cpp | 58 +++++++++++++++++ src/plugins/Matrix.cpp | 4 +- 12 files changed, 316 insertions(+), 108 deletions(-) create mode 100644 include/Utils.hpp create mode 100644 src/Utils.cpp diff --git a/README.md b/README.md index cbdc5a0..5c2436a 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,10 @@ and store it in `$HOME/.config/quickmedia/credentials/mangadex.json` under the k ``` ## Environment variables Set `QM_PHONE_FACTOR=1` to disable the room list side panel in matrix. +## UI scaling +Either set the `GDK_SCALE` environment variable or add `Xft.dpi` to `$HOME/.Xresources` (`xrdb` which is part of the `xorg-xrdb` package needs to be installed).\ +For example a value of 96 for the `Xft.dpi` means 1.0 scaling and 144 (96*1.5) means 1.5 scaling.\ +Note that at the moment, cached images will not be scaled with the dpi. Images do also not scale above their original size. # Dependencies ## Compile See project.conf \[dependencies]. diff --git a/TODO b/TODO index a9aac9d..5685dc6 100644 --- a/TODO +++ b/TODO @@ -37,7 +37,6 @@ Add option to disable autosearch and search when pressing enter instead or somet Sleep when idle, to reduce cpu usage from 1-2% to 0%, important for mobile devices. Also render view to a rendertexture and render that instead of redrawing every time every time. Provide a way to specify when notifications should be received (using matrix api) and also read the notification config from matrix. Also provide a way to disable notifications globally. Use quickmedia to show image in matrix rooms, instead of mpv. -Respect ~/.Xresources dpi (read the file, loop lines and look for Xft.dpi). Fallback to 96 dpi. Merge body items in matrix if they are posted by the same author (there is a git stash for this). Add command to ban users. Support peertube (works with mpv, but need to implement search and related videos). diff --git a/include/Body.hpp b/include/Body.hpp index 1664696..ac5853d 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -14,6 +14,7 @@ namespace sf { class RenderWindow; class Shader; class Texture; + class Event; } namespace QuickMedia { @@ -94,13 +95,7 @@ namespace QuickMedia { dirty_author = true; } - void add_reaction(std::string text, void *userdata) { - sf::String str = sf::String::fromUtf8(text.begin(), text.end()); - Reaction reaction; - reaction.text = std::make_unique(std::move(str), false, 14, 0.0f); - reaction.userdata = userdata; - reactions.push_back(std::move(reaction)); - } + void add_reaction(std::string text, void *userdata); // Returns true if reaction is found bool remove_reaction_by_userdata(void *userdata) { @@ -209,6 +204,9 @@ namespace QuickMedia { BodyItem* get_last_fully_visible_item(); void clamp_selection(); + + // Returns true if the event was handled + bool on_event(const sf::RenderWindow &window, const sf::Event &event); void draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size); void draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress); // |size| is the clip size, another outside this will be cut off. @@ -263,7 +261,7 @@ namespace QuickMedia { std::unordered_map> item_thumbnail_textures; int selected_item; int prev_selected_item; - float page_scroll; + double page_scroll; // TODO: Use a loading gif or something similar instead, to properly indicate the image is loading. Which could also have text that says "failed to load image" when image loading failed. sf::RectangleShape image_fallback; sf::RectangleShape item_separator; @@ -272,13 +270,24 @@ namespace QuickMedia { sf::Sprite loading_icon; int num_visible_items; bool last_item_fully_visible; + int first_fully_visible_item; int last_fully_visible_item; sf::Clock draw_timer; + sf::Clock frame_timer; double elapsed_time_sec = 0.0; bool selected_line_top_visible = true; bool selected_line_bottom_visible = true; bool items_cut_off = false; float offset_to_top = 0.0f; float offset_to_bottom = 0.0f; + bool mouse_state_set = false; + bool mouse_left_pressed = false; + sf::Vector2f mouse_pos; + sf::Vector2f prev_mouse_pos; + sf::Vector2i mouse_pos_raw; + sf::Vector2f mouse_scroll_accel; + float selected_item_height = 0.0f; + float selected_scrolled = 0.0f; + //float scroll_y = 0.0f; }; } \ No newline at end of file diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index 4142a27..21b451d 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -96,6 +96,7 @@ namespace QuickMedia { private: void base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_key_press = true, bool handle_searchbar = true); void event_idle_handler(const sf::Event &event); + void idle_active_handler(); void update_idle_state(); void page_loop_render(sf::RenderWindow &window, std::vector &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters); using PageLoopSubmitHandler = std::function &new_tabs)>; diff --git a/include/Utils.hpp b/include/Utils.hpp new file mode 100644 index 0000000..7ca409c --- /dev/null +++ b/include/Utils.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace QuickMedia { + float get_ui_scale(); +} \ No newline at end of file diff --git a/src/Body.cpp b/src/Body.cpp index 8a32f76..d14a3c5 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -3,26 +3,28 @@ #include "../include/Scale.hpp" #include "../include/ResourceLoader.hpp" #include "../include/AsyncImageLoader.hpp" +#include "../include/Utils.hpp" #include "../plugins/Plugin.hpp" #include +#include #include #include #include static const sf::Color front_color(32, 36, 42); static const sf::Color back_color(33, 35, 37); -static const float spacing_y = 15.0f; -static const float padding_x = 10.0f; -static const float image_padding_x = 5.0f; -static const float padding_y = 5.0f; -static const float embedded_item_padding_y = 0.0f; +static const float spacing_y = 15.0f * QuickMedia::get_ui_scale(); +static const float padding_x = 10.0f * QuickMedia::get_ui_scale(); +static const float image_padding_x = 5.0f * QuickMedia::get_ui_scale(); +static const float padding_y = 5.0f * QuickMedia::get_ui_scale(); +static const float embedded_item_padding_y = 0.0f * QuickMedia::get_ui_scale(); static const double thumbnail_fade_duration_sec = 0.1; -static const float reaction_background_padding_x = 7.0f; -static const float reaction_background_padding_y = 3.0f; -static const float reaction_spacing_x = 5.0f; -static const float reaction_padding_y = 7.0f; -static const int embedded_item_font_size = 14; +static const float reaction_background_padding_x = 7.0f * QuickMedia::get_ui_scale(); +static const float reaction_background_padding_y = 3.0f * QuickMedia::get_ui_scale(); +static const float reaction_spacing_x = 5.0f * QuickMedia::get_ui_scale(); +static const float reaction_padding_y = 7.0f * QuickMedia::get_ui_scale(); +static const int embedded_item_font_size = 14 * QuickMedia::get_ui_scale(); namespace QuickMedia { BodyItem::BodyItem(std::string _title) : @@ -99,9 +101,17 @@ namespace QuickMedia { return *this; } + void BodyItem::add_reaction(std::string text, void *userdata) { + sf::String str = sf::String::fromUtf8(text.begin(), text.end()); + Reaction reaction; + reaction.text = std::make_unique(std::move(str), false, 14 * get_ui_scale(), 0.0f); + reaction.userdata = userdata; + reactions.push_back(std::move(reaction)); + } + Body::Body(Program *program, sf::Texture &loading_icon_texture) : - progress_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 14), - replies_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 14), + progress_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 14 * get_ui_scale()), + replies_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 14 * get_ui_scale()), embedded_item_load_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), embedded_item_font_size), draw_thumbnails(true), wrap_around(false), @@ -116,6 +126,7 @@ namespace QuickMedia { loading_icon(loading_icon_texture), num_visible_items(0), last_item_fully_visible(true), + first_fully_visible_item(-1), last_fully_visible_item(-1) { progress_text.setFillColor(sf::Color::White); @@ -180,6 +191,8 @@ namespace QuickMedia { break; } + // selected_scrolled = 0.0f; + if(selected_item == new_selected_item) return false; selected_item = new_selected_item; @@ -213,6 +226,8 @@ namespace QuickMedia { break; } + //selected_scrolled = 0.0f; + if(selected_item == new_selected_item) return false; selected_item = new_selected_item; @@ -221,6 +236,8 @@ namespace QuickMedia { void Body::set_selected_item(int item, bool reset_prev_selected_item) { //assert(item >= 0 && item < (int)items.size()); + if(item != selected_item) + selected_scrolled = 0.0f; selected_item = item; if(reset_prev_selected_item) prev_selected_item = selected_item; @@ -241,6 +258,8 @@ namespace QuickMedia { } void Body::select_first_item() { + if(selected_item != 0) + selected_scrolled = 0.0f; selected_item = 0; prev_selected_item = selected_item; page_scroll = 0.0f; @@ -248,13 +267,18 @@ namespace QuickMedia { } void Body::select_last_item() { - selected_item = std::max(0, (int)items.size() - 1); + int new_selected_item = std::max(0, (int)items.size() - 1); + if(selected_item != new_selected_item) + selected_scrolled = 0.0f; + selected_item = new_selected_item; //prev_selected_item = selected_item; //page_scroll = 0.0f; clamp_selection(); } void Body::clear_items() { + if(selected_item != 0) + selected_scrolled = 0.0f; items.clear(); selected_item = 0; prev_selected_item = selected_item; @@ -347,6 +371,39 @@ namespace QuickMedia { } } + bool Body::on_event(const sf::RenderWindow &window, const sf::Event &event) { + #if 0 + if(!mouse_state_set) { + mouse_state_set = true; + mouse_left_pressed = sf::Mouse::isButtonPressed(sf::Mouse::Left); + if(mouse_left_pressed) { + mouse_pos_raw = sf::Mouse::getPosition(window); + mouse_pos = sf::Vector2f(mouse_pos_raw.x, mouse_pos_raw.y); + prev_mouse_pos = mouse_pos; + return true; + } + } + + if(event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left && !mouse_left_pressed) { + mouse_left_pressed = true; + mouse_pos_raw.x = event.mouseButton.x; + mouse_pos_raw.y = event.mouseButton.y; + mouse_pos = sf::Vector2f(mouse_pos_raw.x, mouse_pos_raw.y); + prev_mouse_pos = mouse_pos; + return true; + } else if(event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left && mouse_left_pressed) { + mouse_left_pressed = false; + return true; + } else if(event.type == sf::Event::MouseMoved && mouse_left_pressed) { + mouse_pos_raw.x = event.mouseMove.x; + mouse_pos_raw.y = event.mouseMove.y; + return true; + } + + #endif + return false; + } + void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) { draw(window, pos, size, Json::Value::nullSingleton()); } @@ -359,12 +416,55 @@ namespace QuickMedia { const float start_y = pos.y; elapsed_time_sec = draw_timer.getElapsedTime().asSeconds(); + float frame_time = frame_timer.restart().asSeconds(); + if(frame_time > 2.0f) + frame_time = 2.0f; + + const sf::Vector2f mouse_pos_diff(mouse_pos_raw.x - mouse_pos.x, mouse_pos_raw.y - mouse_pos.y); + const float move_speed = 25.0f; + mouse_pos.x += (mouse_pos_diff.x * frame_time * move_speed); + mouse_pos.y += (mouse_pos_diff.y * frame_time * move_speed); + + sf::Vector2f mouse_smooth_diff(mouse_pos.x - prev_mouse_pos.x, mouse_pos.y - prev_mouse_pos.y); + prev_mouse_pos = mouse_pos; + + if(mouse_left_pressed) { + selected_scrolled += mouse_smooth_diff.y; + page_scroll += mouse_smooth_diff.y; + mouse_scroll_accel = mouse_smooth_diff; + } else { + selected_scrolled += mouse_scroll_accel.y; + page_scroll += mouse_scroll_accel.y; + const float scroll_deaccel = 1.02f; + double deaccel = scroll_deaccel * (1.0 + frame_time); + if(deaccel < 0.0001) + deaccel = 1.0; + + mouse_scroll_accel.x /= deaccel; + if(fabs(mouse_scroll_accel.x) < 0.0001) + mouse_scroll_accel.x = 0.0; + + mouse_scroll_accel.y /= deaccel; + if(fabs(mouse_scroll_accel.y) < 0.0001) + mouse_scroll_accel.y = 0.0; + } + + if(selected_item != -1) { + if(selected_scrolled <= -selected_item_height) { + selected_scrolled += selected_item_height; + select_next_item(false); + } else if(selected_scrolled >= selected_item_height) { + selected_scrolled -= selected_item_height; + select_previous_item(false); + } + } //item_background.setFillColor(front_color); //item_background.setOutlineThickness(1.0f); //item_background.setOutlineColor(sf::Color(13, 15, 17)); item_separator.setFillColor(line_separator_color); num_visible_items = 0; + first_fully_visible_item = -1; last_fully_visible_item = -1; selected_line_top_visible = true; selected_line_bottom_visible = true; @@ -424,7 +524,7 @@ namespace QuickMedia { prev_selected_item = selected_item; } - float selected_item_height = get_item_height(items[selected_item].get(), size.x) + spacing_y; + selected_item_height = get_item_height(items[selected_item].get(), size.x) + spacing_y; bool selected_item_fits_on_screen = selected_item_height <= size.y; selected_line_top_visible = pos.y - start_y + page_scroll >= 0.0f; selected_line_bottom_visible = pos.y - start_y + page_scroll + selected_item_height <= size.y; @@ -491,6 +591,7 @@ namespace QuickMedia { draw_item(window, item.get(), prev_pos, size, item_height, i, content_progress); glDisable(GL_SCISSOR_TEST); ++num_visible_items; + first_fully_visible_item = i; } offset_to_top = prev_pos.y - start_y; @@ -535,6 +636,8 @@ namespace QuickMedia { } } + if(first_fully_visible_item == -1) + first_fully_visible_item = selected_item; if(last_fully_visible_item == -1) last_fully_visible_item = selected_item; @@ -571,7 +674,7 @@ namespace QuickMedia { if(body_item->title_text) body_item->title_text->setString(std::move(str)); else - body_item->title_text = std::make_unique(std::move(str), false, 16, width); + body_item->title_text = std::make_unique(std::move(str), false, 16 * get_ui_scale(), width); body_item->title_text->setFillColor(body_item->get_title_color()); body_item->title_text->updateGeometry(); } @@ -582,7 +685,7 @@ namespace QuickMedia { if(body_item->description_text) body_item->description_text->setString(std::move(str)); else - body_item->description_text = std::make_unique(std::move(str), false, 14, width); + body_item->description_text = std::make_unique(std::move(str), false, 14 * get_ui_scale(), width); body_item->description_text->setFillColor(body_item->get_description_color()); body_item->description_text->updateGeometry(); } @@ -593,7 +696,7 @@ namespace QuickMedia { if(body_item->author_text) body_item->author_text->setString(std::move(str)); else - body_item->author_text = std::make_unique(std::move(str), true, 14, width); + body_item->author_text = std::make_unique(std::move(str), true, 14 * get_ui_scale(), width); body_item->author_text->setFillColor(body_item->get_author_color()); body_item->author_text->updateGeometry(); } @@ -621,7 +724,7 @@ namespace QuickMedia { if(body_item->timestamp_text) body_item->timestamp_text->setString(time_str); else - body_item->timestamp_text = std::make_unique(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN), 10); + body_item->timestamp_text = std::make_unique(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN), 10 * get_ui_scale()); body_item->timestamp_text->setFillColor(sf::Color(185, 190, 198, 100)); } @@ -648,10 +751,11 @@ namespace QuickMedia { sf::Vector2i Body::get_item_thumbnail_size(BodyItem *item) const { sf::Vector2i content_size; + sf::Vector2i thumbnail_max_size_scaled(thumbnail_max_size.x * get_ui_scale(), thumbnail_max_size.y * get_ui_scale()); if(item->thumbnail_size.x > 0 && item->thumbnail_size.y > 0) - content_size = clamp_to_size(item->thumbnail_size, thumbnail_max_size); + content_size = clamp_to_size(sf::Vector2i(item->thumbnail_size.x * get_ui_scale(), item->thumbnail_size.y * get_ui_scale()), thumbnail_max_size_scaled); else - content_size = thumbnail_max_size; + content_size = thumbnail_max_size_scaled; return content_size; } @@ -779,9 +883,9 @@ namespace QuickMedia { } } - const float timestamp_text_y = std::floor(item_pos.y + padding_y - 6.0f); + const float timestamp_text_y = std::floor(item_pos.y + padding_y - 6.0f * get_ui_scale()); if(item->author_text) { - item->author_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f)); + item->author_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f * get_ui_scale())); item->author_text->setMaxWidth(size.x - text_offset_x - image_padding_x); item->author_text->draw(window); @@ -798,7 +902,7 @@ namespace QuickMedia { replies_text.setString(std::move(replies_text_str)); window.draw(replies_text); - item_pos.y += item->author_text->getHeight() - 2.0f; + item_pos.y += item->author_text->getHeight() - 2.0f + 3.0f * get_ui_scale(); } if(include_embedded_item && item->embedded_item_status != FetchStatus::NONE) { @@ -826,15 +930,15 @@ namespace QuickMedia { //title_text.setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y)); //window.draw(title_text); if(item->title_text) { - item->title_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f)); + item->title_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f * get_ui_scale())); item->title_text->setMaxWidth(size.x - text_offset_x - image_padding_x); item->title_text->draw(window); - item_pos.y += item->title_text->getHeight() - 2.0f; + item_pos.y += item->title_text->getHeight() - 2.0f + 3.0f * get_ui_scale(); } if(item->description_text) { float height_offset = 0.0f; - item->description_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f + height_offset)); + item->description_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f * get_ui_scale() + height_offset)); item->description_text->setMaxWidth(size.x - text_offset_x - image_padding_x); item->description_text->draw(window); item_pos.y += item->description_text->getHeight() - 2.0f; @@ -860,7 +964,7 @@ namespace QuickMedia { reaction.text->draw(window); if(text_offset_x + reaction_offset_x + reaction.text->getWidth() + reaction_background_padding_x * 2.0f > size.x && i < (int)item->reactions.size() - 1) { reaction_offset_x = 0.0f; - item_pos.y += reaction.text->getHeight() + reaction_padding_y + 6.0f; + item_pos.y += reaction.text->getHeight() + reaction_padding_y + 6.0f * get_ui_scale(); reaction_max_height = reaction.text->getHeight(); } } @@ -884,7 +988,7 @@ namespace QuickMedia { if(current_json.isNumeric() && total_json.isNumeric()) { progress_text.setString(std::string("Page: ") + std::to_string(current_json.asInt()) + "/" + std::to_string(total_json.asInt())); auto bounds = progress_text.getLocalBounds(); - progress_text.setPosition(std::floor(item_pos.x + size.x - bounds.width - padding_x), timestamp_text_y + 6.0f); + progress_text.setPosition(std::floor(item_pos.x + size.x - bounds.width - padding_x), timestamp_text_y + 6.0f * get_ui_scale()); window.draw(progress_text); } } @@ -941,10 +1045,10 @@ namespace QuickMedia { float item_height = 0.0f; if(item->title_text) { - item_height += item->title_text->getHeight() - 2.0f; + item_height += item->title_text->getHeight() - 2.0f + 3.0f * get_ui_scale(); } if(item->author_text) { - item_height += item->author_text->getHeight() - 2.0f; + item_height += item->author_text->getHeight() - 2.0f + 3.0f * get_ui_scale(); } if(include_embedded_item && item->embedded_item_status != FetchStatus::NONE) { if(item->embedded_item) diff --git a/src/Entry.cpp b/src/Entry.cpp index c18006f..facb615 100644 --- a/src/Entry.cpp +++ b/src/Entry.cpp @@ -1,22 +1,23 @@ #include "../include/Entry.hpp" #include "../include/ResourceLoader.hpp" +#include "../include/Utils.hpp" #include #include #include #include -const float background_margin_horizontal = 5.0f; -const float padding_vertical = 3.0f; -const float background_margin_vertical = 0.0f; +const float background_margin_horizontal = 5.0f * QuickMedia::get_ui_scale(); +const float padding_vertical = 3.0f * QuickMedia::get_ui_scale(); +const float background_margin_vertical = 0.0f * QuickMedia::get_ui_scale(); namespace QuickMedia { Entry::Entry(const std::string &placeholder_text) : on_submit_callback(nullptr), draw_background(true), - text("", false, 16, 0.0f), + text("", false, 16 * get_ui_scale(), 0.0f), width(0.0f), background(sf::Vector2f(1.0f, 1.0f), 7.0f, 10), - placeholder(placeholder_text, *FontLoader::get_font(FontLoader::FontType::LATIN), 16) + placeholder(placeholder_text, *FontLoader::get_font(FontLoader::FontType::LATIN), 16 * get_ui_scale()) { text.setEditable(true); background.setFillColor(sf::Color(55, 60, 68)); @@ -74,8 +75,8 @@ namespace QuickMedia { void Entry::set_position(const sf::Vector2f &pos) { background.setPosition(pos); - text.setPosition(pos + sf::Vector2f(background_margin_horizontal, background_margin_vertical - 3.0f)); - placeholder.setPosition(pos + sf::Vector2f(background_margin_horizontal, background_margin_vertical + 3.0f)); + text.setPosition(pos + sf::Vector2f(background_margin_horizontal, background_margin_vertical - 3.0f * get_ui_scale())); + placeholder.setPosition(pos + sf::Vector2f(background_margin_horizontal, background_margin_vertical + 3.0f * get_ui_scale())); } void Entry::set_max_width(float width) { diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index e515c7a..e080cbc 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -25,6 +25,7 @@ #include "../include/SfmlFixes.hpp" #include "../include/ResourceLoader.hpp" #include "../include/AsyncTask.hpp" +#include "../include/Utils.hpp" #include "../external/hash-library/sha256.h" #include @@ -46,10 +47,10 @@ static const sf::Color back_color(21, 25, 30); static const std::string fourchan_google_captcha_api_key = "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"; -static const float tab_text_size = 16.0f; -static const float tab_height = tab_text_size + 10.0f; +static const float tab_text_size = 16.0f * QuickMedia::get_ui_scale(); +static const float tab_height = tab_text_size + 10.0f * QuickMedia::get_ui_scale(); static const sf::Color tab_selected_color(55, 60, 68); -static const float tab_margin_x = 10.0f; +static const float tab_margin_x = 10.0f * QuickMedia::get_ui_scale(); static int FPS_IDLE = 2; static const double IDLE_TIMEOUT_SEC = 2.0; @@ -747,12 +748,15 @@ namespace QuickMedia { } void Program::event_idle_handler(const sf::Event &event) { - if(event.type == sf::Event::KeyPressed || event.type == sf::Event::TextEntered) { - if(idle) - window.setFramerateLimit(monitor_hz); - idle = false; - idle_timer.restart(); - } + if(event.type == sf::Event::KeyPressed || event.type == sf::Event::TextEntered) + idle_active_handler(); + } + + void Program::idle_active_handler() { + if(idle) + window.setFramerateLimit(monitor_hz); + idle = false; + idle_timer.restart(); } void Program::update_idle_state() { @@ -925,12 +929,12 @@ namespace QuickMedia { static void get_body_dimensions(const sf::Vector2f &window_size, SearchBar *search_bar, sf::Vector2f &body_pos, sf::Vector2f &body_size, bool has_tabs = false) { float body_padding_horizontal = 25.0f; - float body_padding_vertical = 25.0f; + float body_padding_vertical = 25.0f * get_ui_scale(); float body_width = window_size.x - body_padding_horizontal * 2.0f; if(body_width <= 480.0f) { body_width = window_size.x; body_padding_horizontal = 0.0f; - body_padding_vertical = 10.0f; + body_padding_vertical = 10.0f * get_ui_scale(); } float tab_h = tab_height; @@ -997,17 +1001,17 @@ namespace QuickMedia { { float shade_extra_height = 0.0f; if(!tabs[selected_tab].search_bar) - shade_extra_height = 10.0f; + shade_extra_height = 10.0f * get_ui_scale(); const float width_per_tab = window_size.x / tabs.size(); tab_background.setSize(sf::Vector2f(std::floor(width_per_tab - tab_margin_x * 2.0f), tab_height)); float tab_vertical_offset = tabs[selected_tab].search_bar ? tabs[selected_tab].search_bar->getBottomWithoutShadow() : 0.0f; tabs[selected_tab].body->draw(window, body_pos, body_size, *json_chapters); - const float tab_y = std::floor(tab_vertical_offset + tab_height * 0.5f - (tab_text_size + 5.0f) * 0.5f) + shade_extra_height; + const float tab_y = std::floor(tab_vertical_offset + tab_height * 0.5f - (tab_text_size + 5.0f * get_ui_scale()) * 0.5f) + shade_extra_height; tab_shade.setPosition(0.0f, std::floor(tab_vertical_offset)); - tab_shade.setSize(sf::Vector2f(window_size.x, shade_extra_height + tab_height + 10.0f)); + tab_shade.setSize(sf::Vector2f(window_size.x, shade_extra_height + tab_height + 10.0f * get_ui_scale())); window.draw(tab_shade); int i = 0; @@ -1076,7 +1080,7 @@ namespace QuickMedia { std::vector tab_associated_data; for(size_t i = 0; i < tabs.size(); ++i) { TabAssociatedData data; - data.search_result_text = sf::Text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 30); + data.search_result_text = sf::Text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 30 * get_ui_scale()); tab_associated_data.push_back(std::move(data)); } @@ -1247,7 +1251,11 @@ namespace QuickMedia { while (window.isOpen() && loop_running) { sf::Int32 frame_time_ms = frame_timer.restart().asMilliseconds(); while (window.pollEvent(event)) { - event_idle_handler(event); + if(tabs[selected_tab].body->on_event(window, event)) + idle_active_handler(); + else + event_idle_handler(event); + if (event.type == sf::Event::Closed) { window.close(); } else if(event.type == sf::Event::Resized) { @@ -2258,7 +2266,7 @@ namespace QuickMedia { sf::Texture image_texture; sf::Sprite image; - sf::Text error_message("", *FontLoader::get_font(FontLoader::FontType::LATIN), 30); + sf::Text error_message("", *FontLoader::get_font(FontLoader::FontType::LATIN), 30 * get_ui_scale()); error_message.setFillColor(sf::Color::White); bool download_in_progress = false; @@ -2322,7 +2330,7 @@ namespace QuickMedia { bool error = !error_message.getString().isEmpty(); bool redraw = true; - sf::Text chapter_text(images_page->manga_name + " | " + images_page->get_chapter_name() + " | Page " + std::to_string(image_index + 1) + "/" + std::to_string(num_images), *FontLoader::get_font(FontLoader::FontType::LATIN), 14); + sf::Text chapter_text(images_page->manga_name + " | " + images_page->get_chapter_name() + " | Page " + std::to_string(image_index + 1) + "/" + std::to_string(num_images), *FontLoader::get_font(FontLoader::FontType::LATIN), 14 * get_ui_scale()); if(image_index == num_images) chapter_text.setString(images_page->manga_name + " | " + images_page->get_chapter_name() + " | End"); chapter_text.setFillColor(sf::Color::White); @@ -2591,7 +2599,7 @@ namespace QuickMedia { sf::Sprite attached_image_sprite; GoogleCaptchaChallengeInfo challenge_info; - sf::Text challenge_description_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 24); + sf::Text challenge_description_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 24 * get_ui_scale()); challenge_description_text.setFillColor(sf::Color::White); const size_t captcha_num_columns = 3; const size_t captcha_num_rows = 3; @@ -2713,14 +2721,14 @@ namespace QuickMedia { comment_input_shade.setFillColor(sf::Color(31, 35, 41)); sf::Sprite logo_sprite(plugin_logo); - logo_sprite.setScale(0.8f, 0.8f); + logo_sprite.setScale(0.8f * get_ui_scale(), 0.8f * get_ui_scale()); sf::Vector2f logo_size(plugin_logo.getSize().x * logo_sprite.getScale().x, plugin_logo.getSize().y * logo_sprite.getScale().y); float prev_chat_height = comment_input.get_height(); float chat_input_height_full = 0.0f; - const float logo_padding_x = 15.0f; - const float chat_input_padding_x = 10.0f; - const float chat_input_padding_y = 10.0f; + const float logo_padding_x = 15.0f * get_ui_scale(); + const float chat_input_padding_x = 10.0f * get_ui_scale(); + const float chat_input_padding_y = 10.0f * get_ui_scale(); sf::Vector2f body_pos; sf::Vector2f body_size; @@ -2733,6 +2741,11 @@ namespace QuickMedia { while (current_page == PageType::IMAGE_BOARD_THREAD && window.isOpen()) { while (window.pollEvent(event)) { + if(navigation_stage == NavigationStage::REPLYING || navigation_stage == NavigationStage::VIEWING_COMMENTS) { + if(thread_body->on_event(window, event)) + idle_active_handler(); + } + event_idle_handler(event); if(navigation_stage == NavigationStage::REPLYING && !frame_skip_text_entry) { comment_input.process_event(event); @@ -2927,7 +2940,7 @@ namespace QuickMedia { comment_input.set_position(sf::Vector2f(std::floor(logo_padding_x + logo_size.x + chat_input_padding_x), chat_input_padding_y)); float body_padding_horizontal = 25.0f; - float body_padding_vertical = 5.0f; + float body_padding_vertical = 5.0f * get_ui_scale(); float body_width = window_size.x - body_padding_horizontal * 2.0f; if(body_width <= 480.0f) { body_width = window_size.x; @@ -3056,7 +3069,7 @@ namespace QuickMedia { SearchBar password_input(nullptr, "Password", true); SearchBar homeserver_input(nullptr, "Homeserver"); - sf::Text status_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 18); + sf::Text status_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 18 * get_ui_scale()); const int num_inputs = 3; SearchBar *inputs[num_inputs] = { &login_input, &password_input, &homeserver_input }; @@ -3095,7 +3108,11 @@ namespace QuickMedia { while (current_page == PageType::CHAT_LOGIN && window.isOpen()) { while (window.pollEvent(event)) { - event_idle_handler(event); + if(body->on_event(window, event)) + idle_active_handler(); + else + event_idle_handler(event); + if (event.type == sf::Event::Closed) { current_page = PageType::EXIT; window.close(); @@ -3270,15 +3287,15 @@ namespace QuickMedia { ChatState chat_state = ChatState::NAVIGATING; std::shared_ptr currently_operating_on_item; - sf::Text replying_to_text("Replying to:", *FontLoader::get_font(FontLoader::FontType::LATIN), 18); + sf::Text replying_to_text("Replying to:", *FontLoader::get_font(FontLoader::FontType::LATIN), 18 * get_ui_scale()); sf::Sprite logo_sprite(plugin_logo); - logo_sprite.setScale(0.8f, 0.8f); + logo_sprite.setScale(0.8f * get_ui_scale(), 0.8f * get_ui_scale()); sf::Vector2f logo_size(plugin_logo.getSize().x * logo_sprite.getScale().x, plugin_logo.getSize().y * logo_sprite.getScale().y); - sf::Text room_name_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD), 18); - const float room_name_text_height = 20.0f; - const float room_name_text_padding_y = 10.0f; + sf::Text room_name_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD), 18 * get_ui_scale()); + const float room_name_text_height = 18.0f * get_ui_scale(); + const float room_name_text_padding_y = 10.0f * get_ui_scale(); const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f; const float room_avatar_height = 32.0f; @@ -4015,9 +4032,9 @@ namespace QuickMedia { float prev_chat_height = chat_input.get_height(); float chat_input_height_full = 0.0f; - const float logo_padding_x = 15.0f; - const float chat_input_padding_x = 10.0f; - const float chat_input_padding_y = 10.0f; + const float logo_padding_x = 15.0f * get_ui_scale(); + const float chat_input_padding_x = 10.0f * get_ui_scale(); + const float chat_input_padding_y = 10.0f * get_ui_scale(); auto launch_url = [this, &video_page, &redraw](const std::string &url) mutable { if(url.empty()) @@ -4268,6 +4285,14 @@ namespace QuickMedia { while (current_page == PageType::CHAT && window.isOpen()) { sf::Int32 frame_time_ms = frame_timer.restart().asMilliseconds(); while (window.pollEvent(event)) { + if(chat_state == ChatState::URL_SELECTION) { + if(url_selection_body.on_event(window, event)) + idle_active_handler(); + } else { + if(tabs[selected_tab].body->on_event(window, event)) + idle_active_handler(); + } + base_event_handler(event, PageType::EXIT, tabs[selected_tab].body.get(), nullptr, false, false); event_idle_handler(event); @@ -4622,7 +4647,7 @@ namespace QuickMedia { float width_ratio = (float)texture_size.x / (float)texture_size.y; float height_scale = room_avatar_height / (float)texture_size.y; float width_scale = height_scale * width_ratio; - room_avatar_sprite.setScale(width_scale, height_scale); + room_avatar_sprite.setScale(width_scale * get_ui_scale(), height_scale * get_ui_scale()); } } @@ -4647,13 +4672,13 @@ namespace QuickMedia { float padding_bottom = 0.0f; if(selected_tab == MESSAGES_TAB_INDEX || selected_tab == PINNED_TAB_INDEX) { room_name_padding_y = 10.0f + room_name_total_height; - tab_vertical_offset = 10.0f; + tab_vertical_offset = 10.0f * get_ui_scale(); } tab_shade_height = tab_spacer_height + std::floor(tab_vertical_offset) + tab_height + room_name_padding_y + padding_bottom; float body_padding_horizontal = 25.0f; - float body_padding_vertical = 5.0f; + float body_padding_vertical = 5.0f * get_ui_scale(); float body_width = window_size.x - body_padding_horizontal * 2.0f; if(body_width <= 480.0f) { body_width = window_size.x; @@ -4661,8 +4686,8 @@ namespace QuickMedia { } this->body_pos = sf::Vector2f(0.0f, tab_shade_height); - if(body_width > 640.0f && show_room_side_panel) { - this->body_size = sf::Vector2f(300.0f, window_size.y - tab_shade_height); + if(window_size.x > 900.0f && show_room_side_panel) { + this->body_size = sf::Vector2f(300.0f * get_ui_scale(), window_size.y - tab_shade_height); draw_room_list = true; } else { this->body_size = sf::Vector2f(0.0f, 0.0f); diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp index 9b5e882..8232033 100644 --- a/src/SearchBar.cpp +++ b/src/SearchBar.cpp @@ -1,6 +1,7 @@ #include "../include/SearchBar.hpp" #include "../include/Scale.hpp" #include "../include/ResourceLoader.hpp" +#include "../include/Utils.hpp" #include #include #include @@ -11,11 +12,11 @@ static const sf::Color text_placeholder_color(255, 255, 255, 100); static const sf::Color front_color(55, 60, 68); -static const float background_margin_horizontal = 15.0f; -static const float PADDING_HORIZONTAL = 25.0f; -static const float padding_top = 10.0f; -static const float padding_bottom = 15.0f; -static const float background_margin_vertical = 4.0f; +static const float background_margin_horizontal = 10.0f + 5.0f * QuickMedia::get_ui_scale(); +static const float PADDING_HORIZONTAL = 25.0f * QuickMedia::get_ui_scale(); +static const float padding_top = 10.0f * QuickMedia::get_ui_scale(); +static const float padding_bottom = 15.0f * QuickMedia::get_ui_scale(); +static const float background_margin_vertical = 4.0f * QuickMedia::get_ui_scale(); namespace QuickMedia { SearchBar::SearchBar(sf::Texture *plugin_logo, const std::string &placeholder, bool input_masked) : @@ -26,8 +27,8 @@ namespace QuickMedia { text_autosearch_delay(0), autocomplete_search_delay(0), caret_visible(true), - text(placeholder, *FontLoader::get_font(FontLoader::FontType::LATIN), 16), - autocomplete_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 16), + text(placeholder, *FontLoader::get_font(FontLoader::FontType::LATIN), 16 * get_ui_scale()), + autocomplete_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 16 * get_ui_scale()), background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10), placeholder_str(placeholder), show_placeholder(true), @@ -144,14 +145,14 @@ namespace QuickMedia { sf::Vector2f texture_size_f(texture_size.x, texture_size.y); sf::Vector2f new_size = wrap_to_size(texture_size_f, sf::Vector2f(200.0f, one_line_height)); plugin_logo_sprite.setScale(get_ratio(texture_size_f, new_size)); - plugin_logo_sprite.setPosition(25.0f, padding_top + vertical_pos); + plugin_logo_sprite.setPosition(25.0f, padding_top + vertical_pos + rect_height * 0.5f - plugin_logo_sprite.getTexture()->getSize().y * plugin_logo_sprite.getScale().y * 0.5f); offset_x = 25.0f + new_size.x + 25.0f; } - const float width = std::floor(window_size.x - offset_x - padding_horizontal); + const float width = std::floor(window_size.x - offset_x - 25.0f); background.setSize(sf::Vector2f(width, rect_height)); shade.setSize(sf::Vector2f(window_size.x, padding_top + rect_height + padding_bottom)); - caret.setSize(sf::Vector2f(2.0f, text.getCharacterSize() + 2.0f)); + caret.setSize(sf::Vector2f(2.0f * get_ui_scale(), text.getCharacterSize() + 2.0f * get_ui_scale())); background_shadow.setSize(sf::Vector2f(window_size.x, 5.0f)); background.setPosition(offset_x, padding_top + vertical_pos); diff --git a/src/Text.cpp b/src/Text.cpp index ca59146..a84a824 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -1,5 +1,6 @@ #include "../include/Text.hpp" #include "../include/ResourceLoader.hpp" +#include "../include/Utils.hpp" #include "../generated/Emoji.hpp" #include #include @@ -11,9 +12,9 @@ namespace QuickMedia { - const float TAB_WIDTH = 4.0f; - - const sf::Color URL_COLOR(15, 192, 252); + static const float TAB_WIDTH = 4.0f; + static const sf::Color URL_COLOR(15, 192, 252); + static const float WORD_WRAP_MIN_SIZE = 80.0f; size_t StringViewUtf32::find(const StringViewUtf32 &other, size_t offset) const { if(offset >= size) @@ -365,10 +366,10 @@ namespace QuickMedia EmojiRectangle emoji_rec = emoji_get_extents(codePoint); const float font_height_offset = -latin_font_height * 1.2f; - sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y + font_height_offset - emoji_rec.height * 0.5f); - sf::Vector2f vertexTopRight(glyphPos.x + emoji_rec.width, glyphPos.y + font_height_offset - emoji_rec.height * 0.5f); - sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y + font_height_offset + emoji_rec.height * 0.5f); - sf::Vector2f vertexBottomRight(glyphPos.x + emoji_rec.width, glyphPos.y + font_height_offset + emoji_rec.height * 0.5f); + sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y + font_height_offset - emoji_rec.height * get_ui_scale() * 0.5f); + sf::Vector2f vertexTopRight(glyphPos.x + emoji_rec.width * get_ui_scale(), glyphPos.y + font_height_offset - emoji_rec.height * get_ui_scale() * 0.5f); + sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y + font_height_offset + emoji_rec.height * get_ui_scale() * 0.5f); + sf::Vector2f vertexBottomRight(glyphPos.x + emoji_rec.width * get_ui_scale(), glyphPos.y + font_height_offset + emoji_rec.height * get_ui_scale() * 0.5f); sf::Vector2f textureTopLeft(emoji_rec.x, emoji_rec.y); sf::Vector2f textureTopRight(emoji_rec.x + emoji_rec.width, emoji_rec.y); @@ -525,7 +526,7 @@ namespace QuickMedia } float vertex_right_side = get_text_quad_right_side(vertex_ref); - if(vertex_right_side > maxWidth) { + if(vertex_right_side > maxWidth && maxWidth > WORD_WRAP_MIN_SIZE) { ++num_lines; // TODO: Ignore line wrap on space if(last_space_index != -1 && last_space_index != i) { @@ -887,10 +888,10 @@ namespace QuickMedia if(!editable) return true; pos.y -= floor(vspace * 2.0f); - const float caret_margin = 2.0f; + const float caret_margin = 2.0f * get_ui_scale(); - sf::RectangleShape caretRect(sf::Vector2f(2.0f, floor(vspace - caret_margin * 2.0f))); - caretRect.setPosition(floor(pos.x + caretPosition.x), floor(pos.y + caretPosition.y + caret_margin + 4.0f)); + sf::RectangleShape caretRect(sf::Vector2f(2.0f * get_ui_scale(), floor(vspace - caret_margin * 2.0f))); + caretRect.setPosition(floor(pos.x + caretPosition.x), floor(pos.y + caretPosition.y + caret_margin + 4.0f * get_ui_scale())); target.draw(caretRect); return true; } diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 0000000..2c3bfb7 --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,58 @@ +#include "../include/Utils.hpp" +#include +#include +#include +#include + +namespace QuickMedia { + static float scale = 1.0f; + static bool scale_set = false; + + static const int XFT_DPI_DEFAULT = 96; + // Returns 96 on error + static int xrdb_get_dpi() { + int xft_dpi = XFT_DPI_DEFAULT; + + FILE *xrdb_query = popen("xrdb -query", "r"); + if(!xrdb_query) + return xft_dpi; + + char line[512]; + while(fgets(line, sizeof(line), xrdb_query)) { + int line_length = strlen(line); + if(line_length > 0 && line[line_length - 1] == '\n') { + line[line_length - 1] = '\0'; + line_length--; + } + + if(line_length > 8 && memcmp(line, "Xft.dpi:", 8) == 0) { + int xft_dpi_file = atoi(line + 8); + if(xft_dpi_file > 0) { + xft_dpi = xft_dpi_file; + break; + } + } + } + + pclose(xrdb_query); + return xft_dpi; + } + + float get_ui_scale() { + if(scale_set) + return scale; + + char *gdk_scale = getenv("GDK_SCALE"); + if(gdk_scale) { + setlocale(LC_ALL, "C"); // Sigh... stupid C + scale = atof(gdk_scale); + if(scale < 0.0001f) + scale = 1.0f; + } else { + scale = (float)xrdb_get_dpi() / (float)XFT_DPI_DEFAULT; + } + + scale_set = true; + return scale; + } +} \ No newline at end of file diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 046fe91..aa6de40 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -3005,7 +3005,7 @@ namespace QuickMedia { auto fetched_message_it = room->fetched_messages_by_event_id.find(event_id); if(fetched_message_it != room->fetched_messages_by_event_id.end()) return fetched_message_it->second; -#if 0 +#if 1 rapidjson::Document request_data(rapidjson::kObjectType); request_data.AddMember("lazy_load_members", true, request_data.GetAllocator()); @@ -3084,7 +3084,7 @@ namespace QuickMedia { events_add_user_info(state_json, room); #endif //events_set_room_name(state_json, room); -#if 0 +#if 1 const rapidjson::Value &event_json = GetMember(json_root, "event"); std::shared_ptr new_message = parse_message_event(event_json, room); #else -- cgit v1.2.3