aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-08-05 21:07:11 +0200
committerdec05eba <dec05eba@protonmail.com>2019-08-05 21:07:14 +0200
commitb894f0b2283a4fcd42fc41f9517b16d623ae3adb (patch)
treebca95c9bcf3947886fd85d1759cb8349a563bfd2 /src
parent2b294258bbc89f1b49554468022a035782e49074 (diff)
Add thumbnails for manganelo
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp196
-rw-r--r--src/QuickMedia.cpp156
-rw-r--r--src/plugins/Manganelo.cpp3
-rw-r--r--src/plugins/Plugin.cpp (renamed from src/Plugin.cpp)6
4 files changed, 202 insertions, 159 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
new file mode 100644
index 0000000..5b58df8
--- /dev/null
+++ b/src/Body.cpp
@@ -0,0 +1,196 @@
+#include "../include/Body.hpp"
+#include "../plugins/Plugin.hpp"
+#include <SFML/Graphics/RectangleShape.hpp>
+#include <SFML/Graphics/Sprite.hpp>
+#include <assert.h>
+
+const sf::Color front_color(43, 45, 47);
+const sf::Color back_color(33, 35, 37);
+
+namespace QuickMedia {
+ Body::Body(sf::Font &font) : title_text("", font, 14), selected_item(0) {
+ title_text.setFillColor(sf::Color::White);
+ }
+
+ void Body::select_previous_item() {
+ if(items.empty())
+ return;
+
+ int num_items = (int)items.size();
+ for(int i = 0; i < num_items; ++i) {
+ --selected_item;
+ if(selected_item < 0)
+ selected_item = num_items - 1;
+ if(items[selected_item]->visible)
+ return;
+ }
+ }
+
+ void Body::select_next_item() {
+ if(items.empty())
+ return;
+
+ int num_items = (int)items.size();
+ for(int i = 0; i < num_items; ++i) {
+ ++selected_item;
+ if(selected_item == num_items)
+ selected_item = 0;
+ if(items[selected_item]->visible)
+ return;
+ }
+ }
+
+ void Body::reset_selected() {
+ selected_item = 0;
+ }
+
+ void Body::clear_items() {
+ items.clear();
+ item_thumbnail_textures.clear();
+ }
+
+ BodyItem* Body::get_selected() const {
+ if(items.empty() || !items[selected_item]->visible)
+ return nullptr;
+ return items[selected_item].get();
+ }
+
+ void Body::clamp_selection() {
+ int num_items = (int)items.size();
+ if(items.empty())
+ return;
+
+ if(selected_item < 0)
+ selected_item = 0;
+ else if(selected_item >= num_items)
+ selected_item = num_items - 1;
+
+ for(int i = selected_item; i >= 0; --i) {
+ if(items[i]->visible) {
+ selected_item = i;
+ return;
+ }
+ }
+
+ for(int i = selected_item; i < num_items; ++i) {
+ if(items[i]->visible) {
+ selected_item = i;
+ return;
+ }
+ }
+ }
+
+ static std::unique_ptr<sf::Texture> load_texture_from_url(const std::string &url) {
+ auto result = std::make_unique<sf::Texture>();
+ result->setSmooth(true);
+ std::string texture_data;
+ if(download_to_string(url, texture_data) == DownloadResult::OK)
+ result->loadFromMemory(texture_data.data(), texture_data.size());
+ return result;
+ }
+
+ // TODO: Skip drawing the rows that are outside the window.
+ // TODO: Use a render target for the whole body so all images can be put into one.
+ // TODO: Only load images once they are visible on the screen.
+ // TODO: Load images asynchronously to prevent scroll lag and to improve load time of suggestions.
+ void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) {
+ const float font_height = title_text.getCharacterSize() + 8.0f;
+ const float image_height = 50.0f;
+
+ sf::RectangleShape image_fallback(sf::Vector2f(50, image_height));
+ image_fallback.setFillColor(sf::Color::White);
+
+ sf::Sprite image;
+
+ sf::RectangleShape item_background;
+ item_background.setFillColor(front_color);
+ item_background.setOutlineThickness(1.0f);
+ item_background.setOutlineColor(sf::Color(63, 65, 67));
+
+ sf::RectangleShape selected_border;
+ selected_border.setFillColor(sf::Color::Red);
+ const float selected_border_width = 5.0f;
+
+ int num_items = items.size();
+ if((int)item_thumbnail_textures.size() != num_items)
+ item_thumbnail_textures.resize(num_items);
+
+ for(int i = 0; i < num_items; ++i) {
+ const auto &item = items[i];
+ assert(items.size() == item_thumbnail_textures.size());
+ auto &item_thumbnail = item_thumbnail_textures[i];
+ if(!item->visible)
+ continue;
+
+ bool draw_thumbnail = !item->thumbnail_url.empty();
+ float row_height = font_height;
+ if(draw_thumbnail) {
+ row_height = image_height;
+ if(!item_thumbnail)
+ item_thumbnail = load_texture_from_url(item->thumbnail_url);
+ }
+
+ sf::Vector2f item_pos = pos;
+ if(i == selected_item) {
+ selected_border.setPosition(pos);
+ selected_border.setSize(sf::Vector2f(selected_border_width, row_height));
+ window.draw(selected_border);
+ item_pos.x += selected_border_width;
+ item_background.setFillColor(front_color);
+ } else {
+ item_background.setFillColor(sf::Color(38, 40, 42));
+ }
+
+ item_background.setPosition(item_pos);
+ item_background.setSize(sf::Vector2f(size.x, row_height));
+ window.draw(item_background);
+
+ float text_offset_x = 0.0f;
+ if(draw_thumbnail) {
+ if(item_thumbnail && item_thumbnail->getNativeHandle() != 0) {
+ image.setTexture(*item_thumbnail, true);
+ auto image_size = image.getTexture()->getSize();
+ auto height_ratio = image_height / image_size.y;
+ auto scale = image.getScale();
+ auto image_scale_ratio = scale.x / scale.y;
+ const float width_ratio = height_ratio * image_scale_ratio;
+ image.setScale(width_ratio, height_ratio);
+ image.setPosition(item_pos);
+ window.draw(image);
+ text_offset_x = width_ratio * image_size.x;
+ } else {
+ image_fallback.setPosition(item_pos);
+ window.draw(image_fallback);
+ }
+ }
+
+ title_text.setString(item->title);
+ title_text.setPosition(item_pos.x + text_offset_x + 10.0f, item_pos.y);
+ window.draw(title_text);
+
+ pos.y += row_height + 10.0f;
+ }
+ }
+
+ //static
+ bool Body::string_find_case_insensitive(const std::string &str, const std::string &substr) {
+ auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(),
+ [](char c1, char c2) {
+ return std::toupper(c1) == std::toupper(c2);
+ });
+ return it != str.end();
+ }
+
+ void Body::filter_search_fuzzy(const std::string &text) {
+ if(text.empty()) {
+ for(auto &item : items) {
+ item->visible = true;
+ }
+ return;
+ }
+
+ for(auto &item : items) {
+ item->visible = string_find_case_insensitive(item->title, text);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 5f65fdd..a867255 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -12,162 +12,6 @@ const sf::Color front_color(43, 45, 47);
const sf::Color back_color(33, 35, 37);
namespace QuickMedia {
- class Body {
- public:
- Body(sf::Font &font) : title_text("", font, 14), selected_item(0) {
- title_text.setFillColor(sf::Color::White);
- }
-
- void add_item(std::unique_ptr<BodyItem> item) {
- items.push_back(std::move(item));
- }
-
- // Select previous item, ignoring invisible items
- void select_previous_item() {
- if(items.empty())
- return;
-
- int num_items = (int)items.size();
- for(int i = 0; i < num_items; ++i) {
- --selected_item;
- if(selected_item < 0)
- selected_item = num_items - 1;
- if(items[selected_item]->visible)
- return;
- }
- }
-
- // Select next item, ignoring invisible items
- void select_next_item() {
- if(items.empty())
- return;
-
- int num_items = (int)items.size();
- for(int i = 0; i < num_items; ++i) {
- ++selected_item;
- if(selected_item == num_items)
- selected_item = 0;
- if(items[selected_item]->visible)
- return;
- }
- }
-
- void reset_selected() {
- selected_item = 0;
- }
-
- void clear_items() {
- items.clear();
- }
-
- BodyItem* get_selected() const {
- if(items.empty() || !items[selected_item]->visible)
- return nullptr;
- return items[selected_item].get();
- }
-
- void clamp_selection() {
- int num_items = (int)items.size();
- if(items.empty())
- return;
-
- if(selected_item < 0)
- selected_item = 0;
- else if(selected_item >= num_items)
- selected_item = num_items - 1;
-
- for(int i = selected_item; i >= 0; --i) {
- if(items[i]->visible) {
- selected_item = i;
- return;
- }
- }
-
- for(int i = selected_item; i < num_items; ++i) {
- if(items[i]->visible) {
- selected_item = i;
- return;
- }
- }
- }
-
- void draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) {
- const float font_height = title_text.getCharacterSize() + 8.0f;
- const float image_height = 50.0f;
-
- sf::RectangleShape image(sf::Vector2f(50, image_height));
- image.setFillColor(sf::Color::White);
-
- sf::RectangleShape item_background;
- item_background.setFillColor(front_color);
- item_background.setOutlineThickness(1.0f);
- item_background.setOutlineColor(sf::Color(63, 65, 67));
-
- sf::RectangleShape selected_border(sf::Vector2f(5.0f, 50));
- selected_border.setFillColor(sf::Color::Red);
-
- int i = 0;
- for(const auto &item : items) {
- if(!item->visible) {
- ++i;
- continue;
- }
-
- sf::Vector2f item_pos = pos;
- if(i == selected_item) {
- selected_border.setPosition(pos);
- window.draw(selected_border);
- item_pos.x += selected_border.getSize().x;
- item_background.setFillColor(front_color);
- } else {
- item_background.setFillColor(sf::Color(38, 40, 42));
- }
-
- item_background.setPosition(item_pos);
- item_background.setSize(sf::Vector2f(size.x, 50));
- window.draw(item_background);
-
- image.setPosition(item_pos);
- window.draw(image);
-
- title_text.setString(item->title);
- title_text.setPosition(item_pos.x + 50 + 10, item_pos.y);
- window.draw(title_text);
-
-
- pos.y += 50 + 10;
- ++i;
- }
- }
-
- static bool string_find_case_insensitive(const std::string &str, const std::string &substr) {
- auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(),
- [](char c1, char c2) {
- return std::toupper(c1) == std::toupper(c2);
- });
- return it != str.end();
- }
-
- // TODO: Make this actually fuzzy... Right now it's just a case insensitive string find.
- // TODO: Highlight the part of the text that matches the search
- void filter_search_fuzzy(const std::string &text) {
- if(text.empty()) {
- for(auto &item : items) {
- item->visible = true;
- }
- return;
- }
-
- for(auto &item : items) {
- item->visible = string_find_case_insensitive(item->title, text);
- }
- }
-
- sf::Text title_text;
- int selected_item;
- std::vector<std::unique_ptr<BodyItem>> items;
- };
-
Program::Program() :
window(sf::VideoMode(800, 600), "QuickMedia"),
window_size(800, 600),
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index 6dd4ec7..e8d24dc 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -85,6 +85,9 @@ namespace QuickMedia {
if(name_str != text) {
auto item = std::make_unique<BodyItem>(name_str);
item->url = "https://manganelo.com/manga/" + url_param_encode(nameunsigned.asString());
+ Json::Value image = child.get("image", "");
+ if(image.isString() && image.asCString()[0] != '\0')
+ item->thumbnail_url = image.asString();
result_items.push_back(std::move(item));
}
}
diff --git a/src/Plugin.cpp b/src/plugins/Plugin.cpp
index c31e715..d87ac34 100644
--- a/src/Plugin.cpp
+++ b/src/plugins/Plugin.cpp
@@ -1,5 +1,5 @@
-#include "../plugins/Plugin.hpp"
-#include "../include/Program.h"
+#include "../../plugins/Plugin.hpp"
+#include "../../include/Program.h"
#include <sstream>
#include <iomanip>
@@ -21,7 +21,7 @@ namespace QuickMedia {
return {};
}
- DownloadResult Plugin::download_to_string(const std::string &url, std::string &result, const std::vector<CommandArg> &additional_args) {
+ DownloadResult download_to_string(const std::string &url, std::string &result, const std::vector<CommandArg> &additional_args) {
std::vector<const char*> args = { "curl", "-H", "Accept-Language: en-US,en;q=0.5", "--compressed", "-s", "-L", url.c_str() };
for(const CommandArg &arg : additional_args) {
args.push_back(arg.option.c_str());