aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--include/Body.hpp1
-rw-r--r--include/QuickMedia.hpp5
-rw-r--r--include/Tabs.hpp47
-rw-r--r--src/Body.cpp16
-rw-r--r--src/QuickMedia.cpp312
-rw-r--r--src/SearchBar.cpp9
-rw-r--r--src/Tabs.cpp200
-rw-r--r--src/plugins/MangaCombined.cpp4
-rw-r--r--src/plugins/MangaGeneric.cpp5
-rw-r--r--src/plugins/Manganelo.cpp2
11 files changed, 408 insertions, 195 deletions
diff --git a/README.md b/README.md
index 0368766..32621cf 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ EXAMPLES:
quickmedia launcher
quickmedia --upscale-images-always manganelo
echo -e "hello\nworld" | quickmedia stdin
- tabbed quickmedia launcher -e
+ tabbed -c -k quickmedia launcher -e
```
## Installation
If you are running arch linux then you can install QuickMedia from aur (https://aur.archlinux.org/packages/quickmedia-git/), otherwise you will need to use [sibs](https://git.dec05eba.com/sibs/) to build QuickMedia manually.
diff --git a/include/Body.hpp b/include/Body.hpp
index 22d9ff9..90e75c8 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -310,6 +310,7 @@ namespace QuickMedia {
float selected_item_height = 0.0f;
float selected_scrolled = 0.0f;
bool loaded_textures_changed = false;
+ std::shared_ptr<BodyItem> clicked_body_item = nullptr;
//float scroll_y = 0.0f;
};
} \ No newline at end of file
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index 9729c4b..bd3b316 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -29,6 +29,7 @@ namespace QuickMedia {
class RoomData;
class MatrixChatPage;
class VideoPage;
+ class Tabs;
enum class ImageViewMode {
SINGLE,
@@ -103,7 +104,7 @@ namespace QuickMedia {
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<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters);
+ void page_loop_render(sf::RenderWindow &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs);
using PageLoopSubmitHandler = std::function<void(const std::vector<Tab> &new_tabs)>;
void page_loop(std::vector<Tab> &tabs, int start_tab_index = 0, PageLoopSubmitHandler after_submit_handler = nullptr);
void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, BodyItems &next_play_items, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = "");
@@ -181,8 +182,6 @@ namespace QuickMedia {
bool fit_image_to_window = false;
RoomData *current_chat_room = nullptr;
bool go_to_previous_page = false;
- sf::RoundedRectangleShape tab_background;
- sf::RectangleShape tab_shade;
sf::Text tab_text;
sf::Vertex gradient_points[4];
sf::Vector2f body_pos;
diff --git a/include/Tabs.hpp b/include/Tabs.hpp
new file mode 100644
index 0000000..d5aa5b6
--- /dev/null
+++ b/include/Tabs.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "../external/RoundedRectangleShape.hpp"
+#include <SFML/Graphics/RectangleShape.hpp>
+#include <SFML/Graphics/Text.hpp>
+#include <vector>
+#include <functional>
+
+namespace sf {
+ class Event;
+ class RenderWindow;
+}
+
+namespace QuickMedia {
+ class Tabs {
+ public:
+ Tabs(sf::Color shade_color = sf::Color(33, 37, 44));
+
+ static float get_height();
+ static float get_shade_height();
+
+ // Returns the id (index) of the tab. The ids start from 0
+ int add_tab(const std::string &title);
+ void on_event(sf::Event &event);
+ void draw(sf::RenderWindow &window, sf::Vector2f pos, float width);
+
+ void set_text(int index, const std::string &text);
+
+ void set_selected(int index);
+ int get_selected() const;
+
+ std::function<void(int)> on_change_tab = nullptr;
+ private:
+ float tab_index_to_x_offset(int index);
+ private:
+ std::vector<sf::Text> tab_texts;
+ sf::RoundedRectangleShape background;
+ sf::RectangleShape shade;
+ int selected_tab = 0;
+ float scroll = 0.0f;
+ float width_per_tab = 0.0f;
+ float tab_background_width = 0.0f;
+ float container_width = 0.0f;
+ int tab_offset = 0;
+ sf::Color shade_color;
+ };
+} \ No newline at end of file
diff --git a/src/Body.cpp b/src/Body.cpp
index 0971e00..c008c40 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -258,9 +258,9 @@ namespace QuickMedia {
if(item != selected_item)
selected_scrolled = 0.0f;
selected_item = item;
+ clamp_selection();
if(reset_prev_selected_item)
prev_selected_item = selected_item;
- clamp_selection();
clamp_selected_item_to_body_count = 1;
//page_scroll = 0.0f;
}
@@ -759,8 +759,6 @@ namespace QuickMedia {
if(!items_cut_off_set)
items_cut_off = false;
- mouse_left_clicked = false;
-
for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) {
if(!it->second->referenced) {
it = item_thumbnail_textures.erase(it);
@@ -784,6 +782,14 @@ namespace QuickMedia {
loaded_textures_changed = false;
malloc_trim(0);
}
+
+ mouse_left_clicked = false;
+ if(clicked_body_item) {
+ auto clicked_body_item_tmp = clicked_body_item; // tmp because below call to body_item_select_callback may call this same draw function
+ clicked_body_item = nullptr;
+ if(body_item_select_callback)
+ body_item_select_callback(clicked_body_item_tmp.get());
+ }
}
void Body::update_dirty_state(BodyItem *body_item, float width) {
@@ -953,12 +959,12 @@ namespace QuickMedia {
item_pos.x = std::floor(pos.x);
item_pos.y = std::floor(pos.y);
- if(body_item_select_callback && mouse_left_clicked) {
+ if(body_item_select_callback && mouse_left_clicked && !clicked_body_item) {
sf::FloatRect item_box(pos, sf::Vector2f(size.x, item_height));
// TODO: Scale mouse_press_pixels_moved_abs with monitor PPI instead of using get_ui_scale()
if(item_box.contains(mouse_click_pos) && item_box.contains(mouse_release_pos) && mouse_press_pixels_moved_abs <= 50.0 * get_ui_scale()) {
+ clicked_body_item = items[item_index];
set_selected_item(item_index, false);
- body_item_select_callback(item);
}
}
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 0e8e197..37c5223 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -28,6 +28,7 @@
#include "../include/SfmlFixes.hpp"
#include "../include/ResourceLoader.hpp"
#include "../include/Utils.hpp"
+#include "../include/Tabs.hpp"
#include "../external/hash-library/sha256.h"
#include <assert.h>
@@ -50,10 +51,6 @@
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 = std::floor(16.0f * QuickMedia::get_ui_scale());
-static const float tab_height = tab_text_size + std::floor(10.0f * QuickMedia::get_ui_scale());
-static const sf::Color tab_selected_color(55, 60, 68);
-static const float tab_margin_x = std::floor(10.0f);
static int FPS_IDLE = 2;
static const double IDLE_TIMEOUT_SEC = 2.0;
static const sf::Vector2i AVATAR_THUMBNAIL_SIZE(std::floor(32 * QuickMedia::get_ui_scale()), std::floor(32 * QuickMedia::get_ui_scale()));
@@ -263,6 +260,7 @@ namespace QuickMedia {
return search_page->submit(title, url, result_tabs);
}
void on_navigate_to_page(Body *body) override {
+ std::string selected_item_url = body->get_selected() ? body->get_selected()->url : "";
body->clear_items();
switch(history_type) {
case HistoryType::YOUTUBE:
@@ -273,6 +271,20 @@ namespace QuickMedia {
break;
}
body->filter_search_fuzzy(search_bar->get_text());
+ int item_to_revert_selection_to = get_body_item_by_url(body, selected_item_url);
+ if(item_to_revert_selection_to != -1)
+ body->set_selected_item(item_to_revert_selection_to, false);
+ }
+
+ // Returns index to item or -1 if not found
+ int get_body_item_by_url(Body *body, const std::string &url) {
+ if(url.empty()) return -1;
+ for(size_t i = 0; i < body->items.size(); ++i) {
+ auto &body_item = body->items[i];
+ if(body_item->url == url)
+ return i;
+ }
+ return -1;
}
private:
Page *search_page;
@@ -403,8 +415,7 @@ namespace QuickMedia {
disp(nullptr),
window_size(1280, 720),
current_page(PageType::EXIT),
- image_index(0),
- tab_background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10)
+ image_index(0)
{
}
@@ -434,7 +445,7 @@ namespace QuickMedia {
fprintf(stderr, " quickmedia launcher\n");
fprintf(stderr, " quickmedia --upscale-images-always manganelo\n");
fprintf(stderr, " echo -e \"hello\\nworld\" | quickmedia stdin\n");
- fprintf(stderr, " tabbed quickmedia launcher -e\n");
+ fprintf(stderr, " tabbed -c -k quickmedia launcher -e\n");
}
static bool is_manga_plugin(const char *plugin_name) {
@@ -846,11 +857,11 @@ namespace QuickMedia {
tabs.push_back(Tab{std::move(history_body), std::move(history_page), std::move(search_bar)});
} else if(strcmp(plugin_name, "manga") == 0) {
auto manganelo = std::make_unique<ManganeloSearchPage>(this);
- auto manganelos = std::make_unique<MangaGenericSearchPage>(this, plugin_name, nullptr);
+ auto manganelos = std::make_unique<MangaGenericSearchPage>(this, "manganelos", nullptr);
add_manganelos_handlers(manganelos.get());
- auto mangatown = std::make_unique<MangaGenericSearchPage>(this, plugin_name, "https://www.mangatown.com");
+ auto mangatown = std::make_unique<MangaGenericSearchPage>(this, "mangatown", "https://www.mangatown.com");
add_mangatown_handlers(mangatown.get());
- auto mangakatana = std::make_unique<MangaGenericSearchPage>(this, plugin_name, "https://mangakatana.com", false);
+ auto mangakatana = std::make_unique<MangaGenericSearchPage>(this, "mangakatana", "https://mangakatana.com", false);
add_mangakatana_handlers(mangakatana.get());
std::vector<MangaPlugin> pages;
@@ -1148,12 +1159,12 @@ namespace QuickMedia {
float body_padding_horizontal = 10.0f;
float body_padding_vertical = std::floor(10.0f);
float body_width = window_size.x - body_padding_horizontal * 2.0f;
- if(body_width <= 480.0f) {
+ /*if(body_width <= 480.0f) {
body_width = window_size.x;
- body_padding_horizontal = 0.0f;
- }
+ body_padding_horizontal = 10.0f;
+ }*/
- float tab_h = tab_height + std::floor(10.0f * get_ui_scale());
+ float tab_h = Tabs::get_shade_height();
if(!search_bar)
tab_h += std::floor(10.0f * get_ui_scale());
@@ -1214,40 +1225,12 @@ namespace QuickMedia {
pipe_selected_text = text;
}
- void Program::page_loop_render(sf::RenderWindow &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters) {
+ void Program::page_loop_render(sf::RenderWindow &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs) {
if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->draw(window, false);
- {
- float shade_extra_height = 0.0f;
- if(!tabs[selected_tab].search_bar)
- shade_extra_height = std::floor(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 + std::floor(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 + std::floor(10.0f * get_ui_scale())));
- window.draw(tab_shade);
-
- int i = 0;
- // TODO: Dont show tabs if there is only one tab
- for(Tab &tab : tabs) {
- if(i == selected_tab) {
- tab_background.setPosition(std::floor(i * width_per_tab + tab_margin_x), std::floor(tab_vertical_offset) + shade_extra_height);
- window.draw(tab_background);
- }
- const float center = (i * width_per_tab) + (width_per_tab * 0.5f);
- // TODO: Optimize. Only set once for each tab!
- tab_text.setString(tab.page->get_title());
- tab_text.setPosition(std::floor(center - tab_text.getLocalBounds().width * 0.5f), std::floor(tab_y));
- window.draw(tab_text);
- ++i;
- }
- }
+ float tab_vertical_offset = tabs[selected_tab].search_bar ? tabs[selected_tab].search_bar->getBottomWithoutShadow() : 0.0f;
+ ui_tabs.draw(window, sf::Vector2f(0.0f, tab_vertical_offset), window_size.x);
+ tabs[selected_tab].body->draw(window, body_pos, body_size, *json_chapters);
if(tab_associated_data.fetching_next_page_running)
window.draw(gradient_points, 4, sf::Quads); // Note: sf::Quads doesn't work with egl
@@ -1285,11 +1268,25 @@ namespace QuickMedia {
malloc_trim(0);
idle = true;
+ bool loop_running = true;
+ bool redraw = true;
+
for(Tab &tab : tabs) {
tab.body->thumbnail_max_size = tab.page->get_thumbnail_max_size();
tab.page->on_navigate_to_page(tab.body.get());
}
+ Tabs ui_tabs;
+ for(auto &tab : tabs) {
+ ui_tabs.add_tab(tab.page->get_title());
+ }
+ ui_tabs.set_selected(start_tab_index);
+
+ ui_tabs.on_change_tab = [&tabs, &redraw](int selected_tab) {
+ tabs[selected_tab].body->clear_cache();
+ redraw = true;
+ };
+
const Json::Value *json_chapters = &Json::Value::nullSingleton();
if(content_storage_json.isObject()) {
const Json::Value &chapters_json = content_storage_json["chapters"];
@@ -1310,19 +1307,14 @@ namespace QuickMedia {
double gradient_inc = 0.0;
const float gradient_height = 5.0f;
- tab_text = sf::Text("", *FontLoader::get_font(FontLoader::FontType::LATIN), tab_text_size);
- int selected_tab = std::min(std::max(0, start_tab_index), (int)tabs.size() - 1);
-
- bool loop_running = true;
- bool redraw = true;
-
auto window_size_u = window.getSize();
window_size.x = window_size_u.x;
window_size.y = window_size_u.y;
std::function<void(const std::string&)> submit_handler;
- submit_handler = [this, &submit_handler, &after_submit_handler, &json_chapters, &tabs, &tab_associated_data, &selected_tab, &loop_running, &redraw](const std::string &search_text) {
+ submit_handler = [this, &submit_handler, &after_submit_handler, &json_chapters, &tabs, &tab_associated_data, &ui_tabs, &loop_running, &redraw](const std::string &search_text) {
+ const int selected_tab = ui_tabs.get_selected();
auto selected_item = tabs[selected_tab].body->get_selected_shared();
if(!selected_item && !tabs[selected_tab].page->allow_submit_no_selection())
return;
@@ -1499,15 +1491,13 @@ namespace QuickMedia {
}
sf::Event event;
-
- tab_shade.setFillColor(sf::Color(33, 37, 44));
- tab_background.setFillColor(tab_selected_color);
-
sf::Clock frame_timer;
while (window.isOpen() && loop_running) {
sf::Int32 frame_time_ms = frame_timer.restart().asMilliseconds();
while (window.pollEvent(event)) {
+ const int selected_tab = ui_tabs.get_selected();
+
if(tabs[selected_tab].body->on_event(window, event))
idle_active_handler();
else
@@ -1525,6 +1515,8 @@ namespace QuickMedia {
tabs[selected_tab].search_bar->on_event(event);
}
+ ui_tabs.on_event(event);
+
if(event.type == sf::Event::Resized || event.type == sf::Event::GainedFocus)
redraw = true;
else if(event.type == sf::Event::KeyPressed) {
@@ -1574,18 +1566,6 @@ namespace QuickMedia {
tabs[selected_tab].body->select_first_item();
} else if(event.key.code == sf::Keyboard::Escape) {
goto page_end;
- } else if(event.key.code == sf::Keyboard::Left || (event.key.control && event.key.code == sf::Keyboard::H)) {
- if(selected_tab > 0) {
- tabs[selected_tab].body->clear_cache();
- --selected_tab;
- redraw = true;
- }
- } else if(event.key.code == sf::Keyboard::Right || (event.key.control && event.key.code == sf::Keyboard::L)) {
- if(selected_tab < (int)tabs.size() - 1) {
- tabs[selected_tab].body->clear_cache();
- ++selected_tab;
- redraw = true;
- }
} else if(event.key.code == sf::Keyboard::Tab) {
if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->set_to_autocomplete();
} else if(event.key.code == sf::Keyboard::Enter) {
@@ -1614,6 +1594,8 @@ namespace QuickMedia {
if(!loop_running || !window.isOpen())
break;
+ const int selected_tab = ui_tabs.get_selected();
+
if(redraw) {
redraw = false;
if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->onWindowResize(window_size);
@@ -1722,7 +1704,7 @@ namespace QuickMedia {
}
window.clear(back_color);
- page_loop_render(window, tabs, selected_tab, tab_associated_data[selected_tab], json_chapters);
+ page_loop_render(window, tabs, selected_tab, tab_associated_data[selected_tab], json_chapters, ui_tabs);
window.display();
if(go_to_previous_page) {
@@ -3215,10 +3197,10 @@ namespace QuickMedia {
float body_padding_horizontal = 10.0f;
float body_padding_vertical = std::floor(10.0f);
float body_width = window_size.x - body_padding_horizontal * 2.0f;
- if(body_width <= 480.0f) {
+ /*if(body_width <= 480.0f) {
body_width = window_size.x;
body_padding_horizontal = 0.0f;
- }
+ }*/
comment_input_shade.setSize(sf::Vector2f(window_size.x, chat_input_height_full));
comment_input_shade.setPosition(0.0f, 0.0f);
@@ -3420,7 +3402,6 @@ namespace QuickMedia {
struct ChatTab {
std::unique_ptr<Body> body;
AsyncTask<BodyItems> future;
- sf::Text text;
};
static const sf::Vector2i CHAT_MESSAGE_THUMBNAIL_MAX_SIZE(600, 337);
@@ -3572,7 +3553,6 @@ namespace QuickMedia {
pinned_tab.body->thumbnail_mask_shader = &circle_mask_shader;
pinned_tab.body->attach_side = AttachSide::BOTTOM;
pinned_tab.body->line_separator_color = sf::Color::Transparent;
- pinned_tab.text = sf::Text("Pinned messages", *FontLoader::get_font(FontLoader::FontType::LATIN), tab_text_size);
tabs.push_back(std::move(pinned_tab));
ChatTab messages_tab;
@@ -3581,7 +3561,6 @@ namespace QuickMedia {
messages_tab.body->thumbnail_mask_shader = &circle_mask_shader;
messages_tab.body->attach_side = AttachSide::BOTTOM;
messages_tab.body->line_separator_color = sf::Color::Transparent;
- messages_tab.text = sf::Text("Messages", *FontLoader::get_font(FontLoader::FontType::LATIN), tab_text_size);
tabs.push_back(std::move(messages_tab));
// ChatTab users_tab;
@@ -3593,14 +3572,56 @@ namespace QuickMedia {
// users_tab.text = sf::Text("Users", *FontLoader::get_font(FontLoader::FontType::LATIN), tab_text_size);
// tabs.push_back(std::move(users_tab));
- const int PINNED_TAB_INDEX = 0;
- const int MESSAGES_TAB_INDEX = 1;
- //const int USERS_TAB_INDEX = 2;
+ Tabs ui_tabs(back_color);
+ const int PINNED_TAB_INDEX = ui_tabs.add_tab("Pinned messages (0)");
+ const int MESSAGES_TAB_INDEX = ui_tabs.add_tab("Messages");
+ ui_tabs.set_selected(MESSAGES_TAB_INDEX);
matrix_chat_page->chat_body = tabs[MESSAGES_TAB_INDEX].body.get();
matrix_chat_page->messages_tab_visible = true;
- int selected_tab = MESSAGES_TAB_INDEX;
+ bool redraw = true;
+
+ sf::Clock read_marker_timer;
+ const sf::Int32 read_marker_timeout_ms_default = 3000;
+ sf::Int32 read_marker_timeout_ms = 0;
+
+ AsyncTask<void> set_read_marker_future;
+ bool setting_read_marker = false;
+
+ sf::Clock start_typing_timer;
+ const double typing_timeout_seconds = 3.0;
+ bool typing = false;
+
+ MessageQueue<bool> typing_state_queue;
+ auto typing_state_handler = [this, &current_room, &typing_state_queue]() {
+ while(true) {
+ std::optional<bool> state_opt = typing_state_queue.pop_wait();
+ if(!state_opt)
+ break;
+
+ bool state = state_opt.value();
+ if(state)
+ matrix->on_start_typing(current_room);
+ else
+ matrix->on_stop_typing(current_room);
+ }
+ };
+ std::thread typing_state_thread(typing_state_handler);
+
+ ui_tabs.on_change_tab = [matrix_chat_page, &redraw, &typing, &typing_state_queue, &read_marker_timer, &tabs, MESSAGES_TAB_INDEX](int selected_tab) {
+ tabs[selected_tab].body->clear_cache();
+ if(selected_tab == MESSAGES_TAB_INDEX)
+ matrix_chat_page->messages_tab_visible = true;
+ read_marker_timer.restart();
+ redraw = true;
+ if(typing) {
+ fprintf(stderr, "Stopped typing\n");
+ typing = false;
+ typing_state_queue.push(false);
+ }
+ };
+
bool is_window_focused = window.hasFocus();
enum class ChatState {
@@ -3633,14 +3654,6 @@ namespace QuickMedia {
sf::Sprite room_avatar_sprite;
auto room_avatar_thumbnail_data = std::make_shared<ThumbnailData>();
- sf::Clock read_marker_timer;
- const sf::Int32 read_marker_timeout_ms_default = 3000;
- sf::Int32 read_marker_timeout_ms = 0;
-
- AsyncTask<void> set_read_marker_future;
- bool setting_read_marker = false;
-
- bool redraw = true;
bool draw_room_list = show_room_side_panel;
// TODO: What if these never end up referencing events? clean up automatically after a while?
@@ -3700,7 +3713,7 @@ namespace QuickMedia {
};
// TODO: Optimize find_body_item_by_event_id hash map?
- auto modify_related_messages_in_current_room = [this, &current_room, &set_body_as_deleted, &unreferenced_events, &tabs](Messages &messages) {
+ auto modify_related_messages_in_current_room = [this, &current_room, &set_body_as_deleted, &unreferenced_events, &tabs, MESSAGES_TAB_INDEX](Messages &messages) {
if(messages.empty())
return;
@@ -3734,7 +3747,7 @@ namespace QuickMedia {
std::vector<std::shared_ptr<Message>> unresolved_reactions;
// TODO: Optimize find_body_item_by_event_id hash map?
- auto process_reactions = [&tabs, &unresolved_reactions, &current_room](Messages &messages) {
+ auto process_reactions = [&tabs, &unresolved_reactions, &current_room, MESSAGES_TAB_INDEX](Messages &messages) {
if(messages.empty())
return;
@@ -3779,7 +3792,7 @@ namespace QuickMedia {
}
};
- auto pinned_body_items_contains_event = [&tabs](const std::string &event_id) {
+ auto pinned_body_items_contains_event = [&tabs, PINNED_TAB_INDEX](const std::string &event_id) {
for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) {
if(static_cast<PinnedEventData*>(body_item->userdata)->event_id == event_id)
return true;
@@ -3787,7 +3800,7 @@ namespace QuickMedia {
return false;
};
- auto process_pinned_events = [&tabs, &pinned_body_items_contains_event](const std::optional<std::vector<std::string>> &pinned_events) {
+ auto process_pinned_events = [&tabs, &ui_tabs, &pinned_body_items_contains_event, PINNED_TAB_INDEX](const std::optional<std::vector<std::string>> &pinned_events) {
if(!pinned_events)
return;
@@ -3821,7 +3834,7 @@ namespace QuickMedia {
else
tabs[PINNED_TAB_INDEX].body->set_selected_item(selected_before);
- tabs[PINNED_TAB_INDEX].text.setString("Pinned messages (" + std::to_string(tabs[PINNED_TAB_INDEX].body->items.size()) + ")");
+ ui_tabs.set_text(PINNED_TAB_INDEX, "Pinned messages (" + std::to_string(tabs[PINNED_TAB_INDEX].body->items.size()) + ")");
};
Body url_selection_body(this, loading_icon);
@@ -3891,7 +3904,7 @@ namespace QuickMedia {
};
std::thread post_thread(post_thread_handler);
- auto message_set_replaced_by = [&tabs, &pending_sent_replies](std::shared_ptr<Message> message) {
+ auto message_set_replaced_by = [&tabs, &pending_sent_replies, MESSAGES_TAB_INDEX](std::shared_ptr<Message> message) {
if(message->related_event_type == RelatedEventType::EDIT) {
auto body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size(), message->related_event_id);
if(body_item) {
@@ -3922,7 +3935,7 @@ namespace QuickMedia {
}
};
- auto upload_file = [this, &tabs, &current_room](const std::string &filepath) {
+ auto upload_file = [this, &tabs, &current_room, MESSAGES_TAB_INDEX](const std::string &filepath) {
TaskResult post_file_result = run_task_with_loading_screen([this, &current_room, filepath]() {
std::string event_id_response;
std::string err_msg;
@@ -3942,11 +3955,12 @@ namespace QuickMedia {
bool frame_skip_text_entry = false;
- chat_input.on_submit_callback = [this, &frame_skip_text_entry, &tabs, &me, &chat_input, &selected_tab, &current_room, &new_page, &chat_state, &pending_sent_replies, &currently_operating_on_item, &post_task_queue, &process_reactions](std::string text) mutable {
+ chat_input.on_submit_callback = [this, &frame_skip_text_entry, &tabs, &me, &chat_input, &ui_tabs, MESSAGES_TAB_INDEX, &current_room, &new_page, &chat_state, &pending_sent_replies, &currently_operating_on_item, &post_task_queue, &process_reactions](std::string text) mutable {
if(!current_room)
return false;
frame_skip_text_entry = true;
+ const int selected_tab = ui_tabs.get_selected();
if(selected_tab == MESSAGES_TAB_INDEX) {
if(text.empty())
@@ -4126,7 +4140,7 @@ namespace QuickMedia {
int fetch_message_tab = -1;
// TODO: How about instead fetching all messages we have, not only the visible ones? also fetch with multiple threads.
- tabs[PINNED_TAB_INDEX].body->body_item_render_callback = [this, &current_room, &me, &fetch_message_future, &tabs, &fetch_body_item, &fetch_message_tab](BodyItem *body_item) {
+ tabs[PINNED_TAB_INDEX].body->body_item_render_callback = [this, &current_room, &me, &fetch_message_future, &tabs, &fetch_body_item, &fetch_message_tab, PINNED_TAB_INDEX, MESSAGES_TAB_INDEX](BodyItem *body_item) {
if(fetch_message_future.valid())
return;
@@ -4200,7 +4214,7 @@ namespace QuickMedia {
bool remove_unread_marker = false;
// TODO: How about instead fetching all messages we have, not only the visible ones? also fetch with multiple threads.
- tabs[MESSAGES_TAB_INDEX].body->body_item_render_callback = [this, &current_room, &me, &remove_unread_marker, &fetch_message_future, &tabs, &is_window_focused, &chat_state, &setting_read_marker, &read_marker_timer, &read_marker_timeout_ms, &set_read_marker_future, &fetch_body_item, &fetch_message_tab](BodyItem *body_item) {
+ tabs[MESSAGES_TAB_INDEX].body->body_item_render_callback = [this, &current_room, &me, &remove_unread_marker, &fetch_message_future, &tabs, &is_window_focused, &chat_state, &setting_read_marker, &read_marker_timer, &read_marker_timeout_ms, &set_read_marker_future, &fetch_body_item, &fetch_message_tab, MESSAGES_TAB_INDEX](BodyItem *body_item) {
Message *message = static_cast<Message*>(body_item->userdata);
if(!message)
return;
@@ -4281,17 +4295,10 @@ namespace QuickMedia {
return false;
};
- const float tab_spacer_height = 0.0f;
sf::Vector2f body_pos;
sf::Vector2f body_size;
sf::Event event;
- sf::RectangleShape tab_shade;
- tab_shade.setFillColor(sf::Color(33, 37, 44));
-
- sf::RoundedRectangleShape tab_background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10);
- tab_background.setFillColor(tab_selected_color);
-
const float gradient_height = 5.0f;
sf::Vertex gradient_points[4];
double gradient_inc = 0;
@@ -4300,7 +4307,7 @@ namespace QuickMedia {
bool fetched_enough_messages = false;
- auto fetch_more_previous_messages_if_needed = [this, &tabs, &current_room, &fetched_enough_messages, &previous_messages_future]() {
+ auto fetch_more_previous_messages_if_needed = [this, &tabs, &current_room, &fetched_enough_messages, &previous_messages_future, MESSAGES_TAB_INDEX]() {
if(!fetched_enough_messages && !previous_messages_future.valid()) {
if(tabs[MESSAGES_TAB_INDEX].body->items.size() < 30) {
previous_messages_future = AsyncTask<Messages>([this, &current_room]() {
@@ -4328,28 +4335,8 @@ namespace QuickMedia {
sf::RectangleShape chat_input_shade;
chat_input_shade.setFillColor(sf::Color(33, 37, 44));
- sf::Clock start_typing_timer;
- const double typing_timeout_seconds = 3.0;
- bool typing = false;
-
float tab_vertical_offset = 0.0f;
- MessageQueue<bool> typing_state_queue;
- auto typing_state_handler = [this, &current_room, &typing_state_queue]() {
- while(true) {
- std::optional<bool> state_opt = typing_state_queue.pop_wait();
- if(!state_opt)
- break;
-
- bool state = state_opt.value();
- if(state)
- matrix->on_start_typing(current_room);
- else
- matrix->on_stop_typing(current_room);
- }
- };
- std::thread typing_state_thread(typing_state_handler);
-
sf::Clock frame_timer;
float prev_chat_height = chat_input.get_height();
@@ -4400,10 +4387,11 @@ namespace QuickMedia {
}
};
- auto add_new_messages_to_current_room = [&me, &tabs, &selected_tab, &current_room](Messages &messages) {
+ auto add_new_messages_to_current_room = [&me, &tabs, &ui_tabs, &current_room, MESSAGES_TAB_INDEX](Messages &messages) {
if(messages.empty())
return;
+ const int selected_tab = ui_tabs.get_selected();
int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size();
bool scroll_to_end = num_items == 0;
if(selected_tab == MESSAGES_TAB_INDEX && (tabs[MESSAGES_TAB_INDEX].body->is_selected_item_last_visible_item() || !tabs[MESSAGES_TAB_INDEX].body->get_selected()))
@@ -4428,10 +4416,11 @@ namespace QuickMedia {
}
};
- auto display_url_or_image = [this, matrix_chat_page, &selected_tab, &redraw, &video_page, &launch_url, &chat_state, &url_selection_body](BodyItem *selected) {
+ auto display_url_or_image = [this, matrix_chat_page, &ui_tabs, &redraw, &video_page, &launch_url, &chat_state, &url_selection_body, PINNED_TAB_INDEX, MESSAGES_TAB_INDEX](BodyItem *selected) {
if(!selected)
return false;
+ const int selected_tab = ui_tabs.get_selected();
Message *selected_item_message = nullptr;
if(selected_tab == MESSAGES_TAB_INDEX) {
selected_item_message = static_cast<Message*>(selected->userdata);
@@ -4480,7 +4469,7 @@ namespace QuickMedia {
return false;
};
- auto update_pinned_messages_author = [&tabs, &current_room](const std::shared_ptr<UserInfo> &user) {
+ auto update_pinned_messages_author = [&tabs, &current_room, PINNED_TAB_INDEX](const std::shared_ptr<UserInfo> &user) {
fprintf(stderr, "updated pinned messages author for user: %s\n", user->user_id.c_str());
for(auto &pinned_body_item : tabs[PINNED_TAB_INDEX].body->items) {
Message *message = static_cast<PinnedEventData*>(pinned_body_item->userdata)->message;
@@ -4492,7 +4481,7 @@ namespace QuickMedia {
}
};
- auto update_messages_author = [&tabs, &current_room](const std::shared_ptr<UserInfo> &user) {
+ auto update_messages_author = [&tabs, &current_room, MESSAGES_TAB_INDEX](const std::shared_ptr<UserInfo> &user) {
fprintf(stderr, "updated messages author for user: %s\n", user->user_id.c_str());
for(auto &message_body_items : tabs[MESSAGES_TAB_INDEX].body->items) {
Message *message = static_cast<Message*>(message_body_items->userdata);
@@ -4504,7 +4493,7 @@ namespace QuickMedia {
};
// TODO: Optimize
- auto update_pinned_messages_authors = [&tabs, &current_room]() {
+ auto update_pinned_messages_authors = [&tabs, &current_room, PINNED_TAB_INDEX]() {
fprintf(stderr, "updated pinned messages author for all users in room: %s\n", current_room->id.c_str());
for(auto &pinned_body_item : tabs[PINNED_TAB_INDEX].body->items) {
Message *message = static_cast<PinnedEventData*>(pinned_body_item->userdata)->message;
@@ -4517,7 +4506,7 @@ namespace QuickMedia {
};
// TODO: Optimize
- auto update_messages_authors = [&tabs, &current_room]() {
+ auto update_messages_authors = [&tabs, &current_room, MESSAGES_TAB_INDEX]() {
fprintf(stderr, "updated messages author for all users in room: %s\n", current_room->id.c_str());
for(auto &message_body_items : tabs[MESSAGES_TAB_INDEX].body->items) {
Message *message = static_cast<Message*>(message_body_items->userdata);
@@ -4528,7 +4517,7 @@ namespace QuickMedia {
}
};
- auto cleanup_tasks = [&set_read_marker_future, &fetch_message_future, &fetch_users_future, &typing_state_queue, &typing_state_thread, &post_task_queue, &provisional_message_queue, &fetched_messages_set, &sent_messages, &pending_sent_replies, &post_thread, &tabs]() {
+ auto cleanup_tasks = [&set_read_marker_future, &fetch_message_future, &fetch_users_future, &typing_state_queue, &typing_state_thread, &post_task_queue, &provisional_message_queue, &fetched_messages_set, &sent_messages, &pending_sent_replies, &post_thread, &tabs, PINNED_TAB_INDEX]() {
set_read_marker_future.cancel();
fetch_message_future.cancel();
fetch_users_future.cancel();
@@ -4574,16 +4563,20 @@ namespace QuickMedia {
}
float tab_shade_height = 0.0f;
+ SyncData sync_data;
room_tabs[room_selected_tab].body->body_item_select_callback = [&move_room](BodyItem *body_item) {
move_room = true;
};
- SyncData sync_data;
-
while (current_page == PageType::CHAT && window.isOpen() && !move_room) {
sf::Int32 frame_time_ms = frame_timer.restart().asMilliseconds();
while (window.pollEvent(event)) {
+ const int selected_tab = ui_tabs.get_selected();
+
+ if(chat_state == ChatState::NAVIGATING)
+ ui_tabs.on_event(event);
+
if(chat_state == ChatState::URL_SELECTION) {
if(url_selection_body.on_event(window, event))
idle_active_handler();
@@ -4680,30 +4673,6 @@ namespace QuickMedia {
tabs[selected_tab].body.get()->select_next_page();
} else if(event.key.code == sf::Keyboard::End) {
tabs[selected_tab].body.get()->select_last_item();
- } else if((event.key.code == sf::Keyboard::Left || (event.key.control && event.key.code == sf::Keyboard::H)) && selected_tab > 0) {
- tabs[selected_tab].body->clear_cache();
- --selected_tab;
- if(selected_tab == MESSAGES_TAB_INDEX)
- matrix_chat_page->messages_tab_visible = true;
- read_marker_timer.restart();
- redraw = true;
- if(typing && current_room) {
- fprintf(stderr, "Stopped typing\n");
- typing = false;
- typing_state_queue.push(false);
- }
- } else if((event.key.code == sf::Keyboard::Right || (event.key.control && event.key.code == sf::Keyboard::L)) && selected_tab < (int)tabs.size() - 1) {
- tabs[selected_tab].body->clear_cache();
- ++selected_tab;
- if(selected_tab == MESSAGES_TAB_INDEX)
- matrix_chat_page->messages_tab_visible = true;
- read_marker_timer.restart();
- redraw = true;
- if(typing && current_room) {
- fprintf(stderr, "Stopped typing\n");
- typing = false;
- typing_state_queue.push(false);
- }
} else if(event.key.code == sf::Keyboard::Escape) {
goto chat_page_end;
}
@@ -4960,6 +4929,8 @@ namespace QuickMedia {
}
}
+ const int selected_tab = ui_tabs.get_selected();
+
float room_name_padding_y = 0.0f;
if(selected_tab == MESSAGES_TAB_INDEX || selected_tab == PINNED_TAB_INDEX)
room_name_padding_y = room_name_total_height;
@@ -4984,15 +4955,15 @@ namespace QuickMedia {
tab_vertical_offset = std::floor(10.0f * get_ui_scale());
}
- tab_shade_height = tab_spacer_height + std::floor(tab_vertical_offset) + tab_height + room_name_padding_y + padding_bottom;
+ tab_shade_height = std::floor(tab_vertical_offset) + Tabs::get_height() + room_name_padding_y + padding_bottom;
float body_padding_horizontal = 10.0f;
float body_padding_vertical = std::floor(10.0f);
float body_width = window_size.x - body_padding_horizontal * 2.0f;
- if(body_width <= 480.0f) {
+ /*if(body_width <= 480.0f) {
body_width = window_size.x;
body_padding_horizontal = 0.0f;
- }
+ }*/
this->body_pos = sf::Vector2f(0.0f, tab_shade_height);
if(window_size.x > 900.0f && show_room_side_panel) {
@@ -5113,18 +5084,12 @@ namespace QuickMedia {
fetch_message_tab = -1;
}
- //chat_input.update();
-
window.clear(back_color);
-
- const float width_per_tab = body_size.x / tabs.size();
- tab_background.setSize(sf::Vector2f(std::floor(width_per_tab - tab_margin_x * 2.0f), tab_height));
if(chat_state == ChatState::URL_SELECTION)
url_selection_body.draw(window, body_pos, body_size);
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) + room_name_padding_y;
//tab_shade.setSize(sf::Vector2f(window_size.x, tab_shade_height));
//window.draw(tab_shade);
@@ -5152,21 +5117,12 @@ namespace QuickMedia {
window.draw(room_list_background);
window.draw(room_label);
const float padding_x = std::floor(10.0f * get_ui_scale());
+ const float tab_y = std::floor(tab_vertical_offset) + room_name_padding_y;
room_tabs[room_selected_tab].body->draw(window, sf::Vector2f(padding_x, tab_y), sf::Vector2f(this->body_size.x - padding_x * 2.0f, window_size.y - tab_y), Json::Value::nullSingleton());
glDisable(GL_SCISSOR_TEST);
}
- int i = 0;
- for(ChatTab &tab : tabs) {
- if(i == selected_tab) {
- tab_background.setPosition(std::floor(body_pos.x + i * width_per_tab + tab_margin_x), tab_spacer_height + std::floor(tab_vertical_offset) + room_name_padding_y);
- window.draw(tab_background);
- }
- const float center = body_pos.x + (i * width_per_tab) + (width_per_tab * 0.5f);
- tab.text.setPosition(std::floor(center - tab.text.getLocalBounds().width * 0.5f), std::floor(tab_y));
- window.draw(tab.text);
- ++i;
- }
+ ui_tabs.draw(window, sf::Vector2f(body_pos.x, std::floor(tab_vertical_offset) + room_name_padding_y), body_size.x);
// TODO: Have one for each room. Also add bottom one? for fetching new messages (currently not implemented, is it needed?)
if(previous_messages_future.valid() && selected_tab == MESSAGES_TAB_INDEX) {
diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp
index 63515bd..fa119a6 100644
--- a/src/SearchBar.cpp
+++ b/src/SearchBar.cpp
@@ -167,10 +167,13 @@ 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 + 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;
+ plugin_logo_sprite.setPosition(10.0f, padding_top + vertical_pos + rect_height * 0.5f - plugin_logo_sprite.getTexture()->getSize().y * plugin_logo_sprite.getScale().y * 0.5f);
+ offset_x = 10.0f + new_size.x + 10.0f;
+ } else {
+ offset_x = 10.0f;
}
- const float width = std::floor(window_size.x - offset_x - 25.0f);
+
+ const float width = std::floor(window_size.x - offset_x - 10.0f);
background.setSize(sf::Vector2f(width, rect_height));
shade.setSize(sf::Vector2f(window_size.x, padding_top + rect_height + padding_bottom));
diff --git a/src/Tabs.cpp b/src/Tabs.cpp
new file mode 100644
index 0000000..464ba4e
--- /dev/null
+++ b/src/Tabs.cpp
@@ -0,0 +1,200 @@
+#include "../include/Tabs.hpp"
+#include "../include/ResourceLoader.hpp"
+#include "../include/Utils.hpp"
+#include <SFML/Window/Event.hpp>
+#include <SFML/Graphics/RenderWindow.hpp>
+#include <SFML/OpenGL.hpp>
+#include <cmath>
+
+namespace QuickMedia {
+ static const float tab_text_size = std::floor(16.0f * QuickMedia::get_ui_scale());
+ static const float tab_height = tab_text_size + std::floor(10.0f * QuickMedia::get_ui_scale());
+ static const sf::Color tab_selected_color(55, 60, 68);
+ static const float tab_min_width = 250.0f;
+ static const float tab_margin_x = 10.0f;
+
+ // static
+ float Tabs::get_height() {
+ return tab_height;
+ }
+
+ // static
+ float Tabs::get_shade_height() {
+ return tab_height + std::floor(10.0f * get_ui_scale());
+ }
+
+ Tabs::Tabs(sf::Color shade_color) : background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10), shade_color(shade_color) {
+ background.setFillColor(tab_selected_color);
+ shade.setFillColor(shade_color);
+ }
+
+ int Tabs::add_tab(const std::string &title) {
+ tab_texts.push_back(sf::Text(title, *FontLoader::get_font(FontLoader::FontType::LATIN), tab_text_size));
+ return tab_texts.size() - 1;
+ }
+
+ void Tabs::on_event(sf::Event &event) {
+ if(event.type == sf::Event::KeyPressed) {
+ if(event.key.code == sf::Keyboard::Left || (event.key.control && event.key.code == sf::Keyboard::H)) {
+ if(selected_tab > 0) {
+ --selected_tab;
+ float scroll_fixed = scroll + (tab_offset * width_per_tab);
+ if(scroll_fixed + tab_index_to_x_offset(selected_tab) + tab_background_width < 0.0f)
+ tab_offset++;
+
+ if(on_change_tab)
+ on_change_tab(selected_tab);
+ }
+ } else if(event.key.code == sf::Keyboard::Right || (event.key.control && event.key.code == sf::Keyboard::L)) {
+ if(selected_tab < (int)tab_texts.size() - 1) {
+ ++selected_tab;
+ float scroll_fixed = scroll + (tab_offset * width_per_tab);
+ if(scroll_fixed + tab_index_to_x_offset(selected_tab) + tab_background_width > container_width)
+ tab_offset--;
+
+ if(on_change_tab)
+ on_change_tab(selected_tab);
+ }
+ }
+ }
+ }
+
+ void Tabs::draw(sf::RenderWindow &window, sf::Vector2f pos, float width) {
+ if(width - tab_margin_x < 0.0f || tab_texts.empty()) return;
+
+ auto window_size = window.getSize();
+ container_width = width;
+
+ const int num_visible_tabs = std::min((int)tab_texts.size(), std::max(1, (int)(width / tab_min_width)));
+ width_per_tab = std::floor(width / num_visible_tabs);
+ const float tab_text_y = std::floor(pos.y + tab_height*0.5f - (tab_text_size + 5.0f*get_ui_scale())*0.5f);
+ tab_background_width = std::floor(width_per_tab - tab_margin_x*2.0f);
+ background.setSize(sf::Vector2f(tab_background_width, tab_height));
+
+ shade.setSize(sf::Vector2f(width, get_shade_height()));
+ shade.setPosition(std::floor(pos.x), std::floor(pos.y));
+ window.draw(shade);
+
+ float scroll_fixed = scroll + (tab_offset * width_per_tab);
+
+ float overflow_last = (scroll_fixed + tab_index_to_x_offset(tab_texts.size() - 1) + tab_background_width) - (width - tab_margin_x);
+ if(overflow_last < 0.0f)
+ scroll_fixed -= overflow_last;
+
+ float overflow = (scroll_fixed + tab_index_to_x_offset(selected_tab) + tab_background_width) - (width - tab_margin_x);
+ float underflow = scroll_fixed + tab_index_to_x_offset(selected_tab);
+ if(overflow > 0.0f)
+ scroll_fixed -= overflow;
+ else if(underflow < 0.0f)
+ scroll_fixed -= underflow;
+
+ bool tabs_cutoff_left = false;
+ bool tabs_cutoff_right = false;
+ const auto start_pos = pos;
+
+ pos.x += scroll_fixed;
+ for(size_t i = 0; i < tab_texts.size(); ++i) {
+ const int index = i;
+ const float background_pos_x = std::floor(pos.x + tab_index_to_x_offset(i));
+ if(background_pos_x - start_pos.x >= width - tab_margin_x) {
+ tabs_cutoff_right = true;
+ break;
+ } else if(background_pos_x + tab_background_width - start_pos.x <= tab_margin_x) {
+ tabs_cutoff_left = true;
+ continue;
+ }
+
+ if((int)index == selected_tab) {
+ background.setPosition(background_pos_x, std::floor(pos.y));
+ window.draw(background);
+ }
+
+ sf::Text &tab_text = tab_texts[index];
+ float text_pos_x = std::floor(pos.x + i*width_per_tab + width_per_tab*0.5f - tab_text.getLocalBounds().width*0.5f);
+ text_pos_x = std::max(text_pos_x, background_pos_x);
+ tab_text.setPosition(text_pos_x, tab_text_y);
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(text_pos_x, (int)window_size.y - (int)tab_text_y - (int)tab_height, tab_background_width, tab_height);
+ window.draw(tab_text);
+ glDisable(GL_SCISSOR_TEST);
+ }
+
+ float lw = std::floor(25.0f * get_ui_scale());
+ float lh = background.getSize().y;
+
+ float line_offset_y = std::floor(lw * 0.35f);
+
+ if(tabs_cutoff_left) {
+ sf::Vertex gradient_points[4];
+ gradient_points[0].position = sf::Vector2f(start_pos.x + tab_margin_x, start_pos.y);
+ gradient_points[1].position = sf::Vector2f(start_pos.x + tab_margin_x + lw, start_pos.y);
+ gradient_points[2].position = sf::Vector2f(start_pos.x + tab_margin_x + lw, start_pos.y + lh);
+ gradient_points[3].position = sf::Vector2f(start_pos.x + tab_margin_x, start_pos.y + lh);
+
+ gradient_points[0].color = shade_color;
+ gradient_points[1].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10);
+ gradient_points[2].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10);
+ gradient_points[3].color = shade_color;
+
+ window.draw(gradient_points, 4, sf::Quads);
+
+ sf::RectangleShape line(sf::Vector2f(std::floor(10.0f * get_ui_scale()), std::floor(2.0f * get_ui_scale())));
+ line.setOrigin(line.getSize().x * 0.5f, line.getSize().y * 0.5f);
+
+ line.rotate(-45.0f);
+ line.setPosition(std::floor(start_pos.x + line.getLocalBounds().width), std::floor(pos.y + background.getSize().y * 0.5f - lh * 0.5f + line_offset_y));
+ window.draw(line);
+
+ line.rotate(-90.0f);
+ line.setPosition(std::floor(start_pos.x + line.getLocalBounds().width), std::floor(pos.y + background.getSize().y * 0.5f - lh * 0.5f + line_offset_y + std::floor(7.0f * get_ui_scale())));
+ window.draw(line);
+ }
+
+ if(tabs_cutoff_right) {
+ sf::Vertex gradient_points[4];
+ gradient_points[0].position = sf::Vector2f(start_pos.x + width - lw - tab_margin_x, start_pos.y);
+ gradient_points[1].position = sf::Vector2f(start_pos.x + width, start_pos.y);
+ gradient_points[2].position = sf::Vector2f(start_pos.x + width, start_pos.y + lh);
+ gradient_points[3].position = sf::Vector2f(start_pos.x + width - lw - tab_margin_x, start_pos.y + lh);
+
+ gradient_points[0].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10);
+ gradient_points[1].color = shade_color;
+ gradient_points[2].color = shade_color;
+ gradient_points[3].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10);
+
+ window.draw(gradient_points, 4, sf::Quads);
+
+ sf::RectangleShape line(sf::Vector2f(std::floor(10.0f * get_ui_scale()), std::floor(2.0f * get_ui_scale())));
+ line.setOrigin(line.getSize().x * 0.5f, line.getSize().y * 0.5f);
+
+ line.rotate(45.0f);
+ line.setPosition(std::floor(start_pos.x + width - lw*0.75f + line.getLocalBounds().width), std::floor(pos.y + background.getSize().y * 0.5f - lh * 0.5f + line_offset_y));
+ window.draw(line);
+
+ line.rotate(-90.0f);
+ line.setPosition(std::floor(start_pos.x + width - lw*0.75f + line.getLocalBounds().width), std::floor(pos.y + background.getSize().y * 0.5f - lh * 0.5f + line_offset_y + std::floor(7.0f * get_ui_scale())));
+ window.draw(line);
+ }
+ }
+
+ void Tabs::set_text(int index, const std::string &text) {
+ if(index < 0 || index >= (int)tab_texts.size()) return;
+ tab_texts[index].setString(sf::String::fromUtf8(text.begin(), text.end()));
+ }
+
+ void Tabs::set_selected(int index) {
+ if(tab_texts.empty()) {
+ selected_tab = 0;
+ } else {
+ selected_tab = std::min(std::max(index, 0), (int)tab_texts.size() - 1);
+ }
+ }
+
+ int Tabs::get_selected() const {
+ return selected_tab;
+ }
+
+ float Tabs::tab_index_to_x_offset(int index) {
+ return std::floor(index*width_per_tab + tab_margin_x);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/MangaCombined.cpp b/src/plugins/MangaCombined.cpp
index 5b80872..e6b8028 100644
--- a/src/plugins/MangaCombined.cpp
+++ b/src/plugins/MangaCombined.cpp
@@ -40,7 +40,7 @@ namespace QuickMedia {
continue;
auto title_item = BodyItem::create("");
- title_item->set_author("======================== " + search_thread.first->title + " ========================");
+ title_item->set_author("------------------------ " + search_thread.first->title + " ------------------------");
result_items.push_back(std::move(title_item));
for(auto &new_body_item : search_page_body_items) {
@@ -65,7 +65,7 @@ namespace QuickMedia {
for(auto &f : plugin_finished_state) {
if(!f.second) {
auto title_item = BodyItem::create("");
- title_item->set_author("======================== " + f.first->title + " timed out ========================");
+ title_item->set_author("------------------------ " + f.first->title + " timed out ------------------------");
result_items.push_back(std::move(title_item));
}
}
diff --git a/src/plugins/MangaGeneric.cpp b/src/plugins/MangaGeneric.cpp
index 8a17040..24472e5 100644
--- a/src/plugins/MangaGeneric.cpp
+++ b/src/plugins/MangaGeneric.cpp
@@ -87,8 +87,9 @@ namespace QuickMedia {
if(merge_userdata->type == MergeType::THUMBNAIL) {
(*body_item_image_context.body_items)[body_item_image_context.index]->thumbnail_url = strip(field_value);
} else if(merge_userdata->type == MergeType::DESCRIPTION) {
- std::string uploaded_date = strip(field_value);
- (*body_item_image_context.body_items)[body_item_image_context.index]->set_description(merge_userdata->desc_prefix ? merge_userdata->desc_prefix : "" + uploaded_date);
+ std::string field_stripped = strip(field_value);
+ const char *prefix = merge_userdata->desc_prefix ? merge_userdata->desc_prefix : "";
+ (*body_item_image_context.body_items)[body_item_image_context.index]->set_description(prefix + field_stripped);
(*body_item_image_context.body_items)[body_item_image_context.index]->set_description_color(sf::Color(179, 179, 179));
}
body_item_image_context.index++;
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index 6d0447a..f8f604d 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -125,7 +125,7 @@ namespace QuickMedia {
auto item = BodyItem::create(strip(name_str));
item->url = "https://manganelo.com/manga/" + url_param_encode(nameunsigned.asString());
if(lastchapter.isString() && lastchapter.asCString()[0] != '\0') {
- item->set_description("Last chapter: " + lastchapter.asString());
+ item->set_description("Latest chapter: " + lastchapter.asString());
item->set_description_color(sf::Color(179, 179, 179));
}
Json::Value image = child.get("image", "");