aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp2
-rw-r--r--src/Entry.cpp2
-rw-r--r--src/ImageViewer.cpp2
-rw-r--r--src/QuickMedia.cpp4
-rw-r--r--src/ResourceLoader.cpp (renamed from src/FontLoader.cpp)29
-rw-r--r--src/SearchBar.cpp2
-rw-r--r--src/Text.cpp71
-rw-r--r--src/plugins/Fourchan.cpp266
-rw-r--r--src/plugins/Matrix.cpp6
9 files changed, 233 insertions, 151 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index b456841..5c2a66c 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -1,7 +1,7 @@
#include "../include/Body.hpp"
#include "../include/QuickMedia.hpp"
#include "../include/Scale.hpp"
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
#include "../plugins/Plugin.hpp"
#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/OpenGL.hpp>
diff --git a/src/Entry.cpp b/src/Entry.cpp
index cc05aa0..c18006f 100644
--- a/src/Entry.cpp
+++ b/src/Entry.cpp
@@ -1,5 +1,5 @@
#include "../include/Entry.hpp"
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Window/Event.hpp>
diff --git a/src/ImageViewer.cpp b/src/ImageViewer.cpp
index be03781..8704c15 100644
--- a/src/ImageViewer.cpp
+++ b/src/ImageViewer.cpp
@@ -2,7 +2,7 @@
#include "../include/Notification.hpp"
#include "../include/Storage.hpp"
#include "../include/SfmlFixes.hpp"
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
#include "../plugins/Manga.hpp"
#include <cmath>
#include <SFML/Window/Event.hpp>
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index c9ab3e1..f494d35 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -20,7 +20,7 @@
#include "../include/Entry.hpp"
#include "../include/NetUtils.hpp"
#include "../include/SfmlFixes.hpp"
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
#include "../include/AsyncTask.hpp"
#include "../external/hash-library/sha256.h"
@@ -340,6 +340,8 @@ namespace QuickMedia {
resources_root = "../../../";
}
+ set_resource_loader_root_path(resources_root.c_str());
+
if(!circle_mask_shader.loadFromFile(resources_root + "shaders/circle_mask.glsl", sf::Shader::Type::Fragment)) {
fprintf(stderr, "Failed to load %s/shaders/circle_mask.glsl", resources_root.c_str());
abort();
diff --git a/src/FontLoader.cpp b/src/ResourceLoader.cpp
index ca33377..9a6060d 100644
--- a/src/FontLoader.cpp
+++ b/src/ResourceLoader.cpp
@@ -1,9 +1,19 @@
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
#include <SFML/Graphics/Font.hpp>
+#include <SFML/Graphics/Texture.hpp>
#include <array>
+#include <unordered_map>
#include <assert.h>
+static std::string resource_root;
static std::array<std::unique_ptr<sf::Font>, 3> font_cache;
+static std::unordered_map<std::string, std::unique_ptr<sf::Texture>> texture_cache;
+
+namespace QuickMedia {
+ void set_resource_loader_root_path(const char *new_resource_root) {
+ resource_root = new_resource_root;
+ }
+}
namespace QuickMedia::FontLoader {
sf::Font* get_font(FontType font_type) {
@@ -49,4 +59,21 @@ namespace QuickMedia::FontLoader {
}
return font;
}
+}
+
+namespace QuickMedia::TextureLoader {
+ sf::Texture* get_texture(const char *filepath) {
+ assert(!resource_root.empty());
+ std::string str = filepath;
+ auto it = texture_cache.find(str);
+ if(it != texture_cache.end())
+ return it->second.get();
+
+ auto new_texture = std::make_unique<sf::Texture>();
+ sf::Texture *result = new_texture.get();
+ if(!new_texture->loadFromFile(resource_root + str))
+ fprintf(stderr, "Failed to load image: %s%s\n", resource_root.c_str(), filepath);
+ texture_cache[str] = std::move(new_texture);
+ return result;
+ }
} \ No newline at end of file
diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp
index dea9951..9b02903 100644
--- a/src/SearchBar.cpp
+++ b/src/SearchBar.cpp
@@ -1,6 +1,6 @@
#include "../include/SearchBar.hpp"
#include "../include/Scale.hpp"
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
#include <SFML/Window/Event.hpp>
#include <SFML/Window/Clipboard.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
diff --git a/src/Text.cpp b/src/Text.cpp
index b463f92..539f211 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -1,5 +1,6 @@
#include "../include/Text.hpp"
-#include "../include/FontLoader.hpp"
+#include "../include/ResourceLoader.hpp"
+#include "../generated/Emoji.hpp"
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Window/Clipboard.hpp>
#include <SFML/Window/Event.hpp>
@@ -46,6 +47,7 @@ namespace QuickMedia
{
vertices[0].setPrimitiveType(sf::PrimitiveType::Quads);
vertices[1].setPrimitiveType(sf::PrimitiveType::Quads);
+ vertices[2].setPrimitiveType(sf::PrimitiveType::Quads);
setString(std::move(_str));
}
@@ -226,9 +228,17 @@ namespace QuickMedia
return size;
}
- static size_t find_end_of_non_cjk(const sf::Uint32 *str, size_t size) {
+ static size_t find_end_of_emoji(const sf::Uint32 *str, size_t size) {
for(size_t i = 0; i < size; ++i) {
- if(is_cjk_codepoint(str[i]))
+ if(!codepoint_is_emoji(str[i]))
+ return i;
+ }
+ return size;
+ }
+
+ static size_t find_end_of_non_cjk_and_non_emoji(const sf::Uint32 *str, size_t size) {
+ for(size_t i = 0; i < size; ++i) {
+ if(is_cjk_codepoint(str[i]) || codepoint_is_emoji(str[i]))
return i;
}
return size;
@@ -240,13 +250,18 @@ namespace QuickMedia
size_t size = str.getSize();
while(index < size) {
size_t offset;
- bool is_cjk = is_cjk_codepoint(str[index]);
- if(is_cjk)
+ TextElement::TextType text_type = TextElement::TextType::LATIN;
+ if(is_cjk_codepoint(str[index])) {
+ text_type = TextElement::TextType::CJK;
offset = find_end_of_cjk(str.getData() + index + 1, size - index - 1);
- else
- offset = find_end_of_non_cjk(str.getData() + index + 1, size - index - 1);
+ } else if(codepoint_is_emoji(str[index])) {
+ text_type = TextElement::TextType::EMOJI;
+ offset = find_end_of_emoji(str.getData() + index + 1, size - index - 1);
+ } else {
+ offset = find_end_of_non_cjk_and_non_emoji(str.getData() + index + 1, size - index - 1);
+ }
textElements.push_back({ StringViewUtf32(str.getData() + index, offset + 1), TextElement::Type::TEXT });
- textElements.back().text_type = is_cjk ? TextElement::TextType::CJK : TextElement::TextType::LATIN;
+ textElements.back().text_type = text_type;
index += 1 + offset;
}
}
@@ -317,6 +332,7 @@ namespace QuickMedia
vertices_linear.clear();
vertices[0].clear();
vertices[1].clear();
+ vertices[2].clear();
boundingBox = sf::FloatRect();
sf::Font *latin_font;
@@ -325,7 +341,8 @@ namespace QuickMedia
else
latin_font = FontLoader::get_font(FontLoader::FontType::LATIN);
- float hspace = latin_font->getGlyph(' ', characterSize, false).advance + characterSpacing;
+ float latin_font_height = latin_font->getGlyph(' ', characterSize, false).advance;
+ float hspace = latin_font_height + characterSpacing;
float vspace = latin_font->getLineSpacing(characterSize); // TODO: What about japanese font???
sf::Vector2f glyphPos;
@@ -338,6 +355,35 @@ namespace QuickMedia
if(textElement.text_type == TextElement::TextType::CJK) {
ff = FontLoader::get_font(FontLoader::FontType::CJK);
vertices_index = 1;
+ } else if(textElement.text_type == TextElement::TextType::EMOJI) {
+ vertices_index = 2;
+ textElement.position = glyphPos;
+ for(size_t i = 0; i < textElement.text.size; ++i)
+ {
+ sf::Uint32 codePoint = textElement.text[i];
+ int vertexStart = vertices[vertices_index].getVertexCount();
+ EmojiRectangle emoji_rec = emoji_get_extents(codePoint);
+
+ const float font_height_offset = -latin_font_height * 1.0f;
+ 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 textureTopLeft(emoji_rec.x, emoji_rec.y);
+ sf::Vector2f textureTopRight(emoji_rec.x + emoji_rec.width, emoji_rec.y);
+ sf::Vector2f textureBottomLeft(emoji_rec.x, emoji_rec.y + emoji_rec.height);
+ sf::Vector2f textureBottomRight(emoji_rec.x + emoji_rec.width, emoji_rec.y + emoji_rec.height);
+
+ vertices[vertices_index].append({ vertexTopLeft, sf::Color::White, textureTopLeft });
+ vertices[vertices_index].append({ vertexTopRight, sf::Color::White, textureTopRight });
+ vertices[vertices_index].append({ vertexBottomRight, sf::Color::White, textureBottomRight });
+ vertices[vertices_index].append({ vertexBottomLeft, sf::Color::White, textureBottomLeft });
+
+ glyphPos.x += emoji_rec.width + characterSpacing;
+ vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
+ }
+ continue;
}
//vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * textElement.text.size); // TODO: Precalculate
@@ -818,6 +864,13 @@ namespace QuickMedia
states.texture = &font->getTexture(characterSize);
target.draw(vertices[i], states);
}
+
+ if(vertices[2].getVertexCount() > 0) {
+ sf::RenderStates states;
+ states.transform.translate(pos);
+ states.texture = TextureLoader::get_texture("images/emoji.png");
+ target.draw(vertices[2], states);
+ }
if(!editable) return true;
pos.y -= floor(vspace * 2.0f);
diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp
index d042a2a..2c62ba1 100644
--- a/src/plugins/Fourchan.cpp
+++ b/src/plugins/Fourchan.cpp
@@ -135,139 +135,7 @@ namespace QuickMedia {
}
PluginResult FourchanBoardsPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- Json::Value json_root;
- DownloadResult result = download_json(json_root, fourchan_url + url + "/catalog.json", {}, true);
- if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
-
- if(!json_root.isArray())
- return PluginResult::ERR;
-
- BodyItems result_items;
-
- for(const Json::Value &page_data : json_root) {
- if(!page_data.isObject())
- continue;
-
- const Json::Value &threads = page_data["threads"];
- if(!threads.isArray())
- continue;
-
- for(const Json::Value &thread : threads) {
- if(!thread.isObject())
- continue;
-
- const Json::Value &sub = thread["sub"];
- const char *sub_begin = "";
- const char *sub_end = sub_begin;
- sub.getString(&sub_begin, &sub_end);
-
- const Json::Value &com = thread["com"];
- const char *comment_begin = "";
- const char *comment_end = comment_begin;
- com.getString(&comment_begin, &comment_end);
-
- const Json::Value &thread_num = thread["no"];
- if(!thread_num.isNumeric())
- continue;
-
- std::string title_text;
- extract_comment_pieces(sub_begin, sub_end - sub_begin,
- [&title_text](const CommentPiece &cp) {
- switch(cp.type) {
- case CommentPiece::Type::TEXT:
- title_text.append(cp.text.data, cp.text.size);
- break;
- case CommentPiece::Type::QUOTE:
- title_text += '>';
- title_text.append(cp.text.data, cp.text.size);
- //comment_text += '\n';
- break;
- case CommentPiece::Type::QUOTELINK: {
- title_text.append(cp.text.data, cp.text.size);
- break;
- }
- case CommentPiece::Type::LINE_CONTINUE: {
- if(!title_text.empty() && title_text.back() == '\n') {
- title_text.pop_back();
- }
- break;
- }
- }
- }
- );
- if(!title_text.empty() && title_text.back() == '\n')
- title_text.back() = ' ';
- html_unescape_sequences(title_text);
-
- std::string comment_text;
- extract_comment_pieces(comment_begin, comment_end - comment_begin,
- [&comment_text](const CommentPiece &cp) {
- switch(cp.type) {
- case CommentPiece::Type::TEXT:
- comment_text.append(cp.text.data, cp.text.size);
- break;
- case CommentPiece::Type::QUOTE:
- comment_text += '>';
- comment_text.append(cp.text.data, cp.text.size);
- //comment_text += '\n';
- break;
- case CommentPiece::Type::QUOTELINK: {
- comment_text.append(cp.text.data, cp.text.size);
- break;
- }
- case CommentPiece::Type::LINE_CONTINUE: {
- if(!comment_text.empty() && comment_text.back() == '\n') {
- comment_text.pop_back();
- }
- break;
- }
- }
- }
- );
- html_unescape_sequences(comment_text);
- // TODO: Do the same when wrapping is implemented
- // TODO: Remove this
- int num_lines = 0;
- for(size_t i = 0; i < comment_text.size(); ++i) {
- if(comment_text[i] == '\n') {
- ++num_lines;
- if(num_lines == 6) {
- comment_text = comment_text.substr(0, i) + " (...)";
- break;
- }
- }
- }
- auto body_item = BodyItem::create(std::move(comment_text));
- body_item->set_author(std::move(title_text));
- body_item->url = std::to_string(thread_num.asInt64());
-
- const Json::Value &ext = thread["ext"];
- const Json::Value &tim = thread["tim"];
- if(tim.isNumeric() && ext.isString()) {
- std::string ext_str = ext.asString();
- if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg" || ext_str == ".webm" || ext_str == ".mp4" || ext_str == ".gif") {
- } else {
- fprintf(stderr, "TODO: Support file extension: %s\n", ext_str.c_str());
- }
- // "s" means small, that's the url 4chan uses for thumbnails.
- // thumbnails always has .jpg extension even if they are gifs or webm.
- body_item->thumbnail_url = fourchan_image_url + url + "/" + std::to_string(tim.asInt64()) + "s.jpg";
-
- sf::Vector2i thumbnail_size(64, 64);
- const Json::Value &tn_w = thread["tn_w"];
- const Json::Value &tn_h = thread["tn_h"];
- if(tn_w.isNumeric() && tn_h.isNumeric())
- thumbnail_size = sf::Vector2i(tn_w.asInt() * 0.5, tn_h.asInt() * 0.5);
- body_item->thumbnail_size = std::move(thumbnail_size);
- }
-
- result_items.push_back(std::move(body_item));
- }
- }
-
- auto body = create_body();
- body->items = std::move(result_items);
- result_tabs.push_back(Tab{std::move(body), std::make_unique<FourchanThreadListPage>(program, title, url), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ result_tabs.push_back(Tab{create_body(), std::make_unique<FourchanThreadListPage>(program, title, url), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
return PluginResult::OK;
}
@@ -462,6 +330,138 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ PluginResult FourchanThreadListPage::lazy_fetch(BodyItems &result_items) {
+ Json::Value json_root;
+ DownloadResult result = download_json(json_root, fourchan_url + board_id + "/catalog.json?s=Index", {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+
+ if(!json_root.isArray())
+ return PluginResult::ERR;
+
+ for(const Json::Value &page_data : json_root) {
+ if(!page_data.isObject())
+ continue;
+
+ const Json::Value &threads = page_data["threads"];
+ if(!threads.isArray())
+ continue;
+
+ for(const Json::Value &thread : threads) {
+ if(!thread.isObject())
+ continue;
+
+ const Json::Value &sub = thread["sub"];
+ const char *sub_begin = "";
+ const char *sub_end = sub_begin;
+ sub.getString(&sub_begin, &sub_end);
+
+ const Json::Value &com = thread["com"];
+ const char *comment_begin = "";
+ const char *comment_end = comment_begin;
+ com.getString(&comment_begin, &comment_end);
+
+ const Json::Value &thread_num = thread["no"];
+ if(!thread_num.isNumeric())
+ continue;
+
+ std::string title_text;
+ extract_comment_pieces(sub_begin, sub_end - sub_begin,
+ [&title_text](const CommentPiece &cp) {
+ switch(cp.type) {
+ case CommentPiece::Type::TEXT:
+ title_text.append(cp.text.data, cp.text.size);
+ break;
+ case CommentPiece::Type::QUOTE:
+ title_text += '>';
+ title_text.append(cp.text.data, cp.text.size);
+ //comment_text += '\n';
+ break;
+ case CommentPiece::Type::QUOTELINK: {
+ title_text.append(cp.text.data, cp.text.size);
+ break;
+ }
+ case CommentPiece::Type::LINE_CONTINUE: {
+ if(!title_text.empty() && title_text.back() == '\n') {
+ title_text.pop_back();
+ }
+ break;
+ }
+ }
+ }
+ );
+ if(!title_text.empty() && title_text.back() == '\n')
+ title_text.back() = ' ';
+ html_unescape_sequences(title_text);
+
+ std::string comment_text;
+ extract_comment_pieces(comment_begin, comment_end - comment_begin,
+ [&comment_text](const CommentPiece &cp) {
+ switch(cp.type) {
+ case CommentPiece::Type::TEXT:
+ comment_text.append(cp.text.data, cp.text.size);
+ break;
+ case CommentPiece::Type::QUOTE:
+ comment_text += '>';
+ comment_text.append(cp.text.data, cp.text.size);
+ //comment_text += '\n';
+ break;
+ case CommentPiece::Type::QUOTELINK: {
+ comment_text.append(cp.text.data, cp.text.size);
+ break;
+ }
+ case CommentPiece::Type::LINE_CONTINUE: {
+ if(!comment_text.empty() && comment_text.back() == '\n') {
+ comment_text.pop_back();
+ }
+ break;
+ }
+ }
+ }
+ );
+ html_unescape_sequences(comment_text);
+ // TODO: Do the same when wrapping is implemented
+ // TODO: Remove this
+ int num_lines = 0;
+ for(size_t i = 0; i < comment_text.size(); ++i) {
+ if(comment_text[i] == '\n') {
+ ++num_lines;
+ if(num_lines == 6) {
+ comment_text = comment_text.substr(0, i) + " (...)";
+ break;
+ }
+ }
+ }
+ auto body_item = BodyItem::create(std::move(comment_text));
+ body_item->set_author(std::move(title_text));
+ body_item->url = std::to_string(thread_num.asInt64());
+
+ const Json::Value &ext = thread["ext"];
+ const Json::Value &tim = thread["tim"];
+ if(tim.isNumeric() && ext.isString()) {
+ std::string ext_str = ext.asString();
+ if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg" || ext_str == ".webm" || ext_str == ".mp4" || ext_str == ".gif") {
+ } else {
+ fprintf(stderr, "TODO: Support file extension: %s\n", ext_str.c_str());
+ }
+ // "s" means small, that's the url 4chan uses for thumbnails.
+ // thumbnails always has .jpg extension even if they are gifs or webm.
+ body_item->thumbnail_url = fourchan_image_url + board_id + "/" + std::to_string(tim.asInt64()) + "s.jpg";
+
+ sf::Vector2i thumbnail_size(64, 64);
+ const Json::Value &tn_w = thread["tn_w"];
+ const Json::Value &tn_h = thread["tn_h"];
+ if(tn_w.isNumeric() && tn_h.isNumeric())
+ thumbnail_size = sf::Vector2i(tn_w.asInt() * 0.5, tn_h.asInt() * 0.5);
+ body_item->thumbnail_size = std::move(thumbnail_size);
+ }
+
+ result_items.push_back(std::move(body_item));
+ }
+ }
+
+ return PluginResult::OK;
+ }
+
PluginResult FourchanThreadPage::login(const std::string &token, const std::string &pin, std::string &response_msg) {
response_msg.clear();
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 1d0e1df..6dabad9 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -455,7 +455,7 @@ namespace QuickMedia {
});
}
- void MatrixQuickMedia::update_room_description(RoomData *room, Messages &new_messages, bool is_initial_sync) {
+ void MatrixQuickMedia::update_room_description(RoomData *room, Messages &new_messages, bool is_initial_sync, bool sync_is_cache) {
time_t read_marker_message_timestamp = 0;
std::shared_ptr<UserInfo> me = matrix->get_me(room);
if(me) {
@@ -485,7 +485,7 @@ namespace QuickMedia {
if(!room_body_item)
return;
- if(last_unread_message) {
+ if(last_unread_message && !sync_is_cache) {
std::string room_desc = "Unread: " + matrix->message_get_author_displayname(last_unread_message) + ": " + extract_first_line_elipses(last_unread_message->body, 150);
int unread_notification_count = room->unread_notification_count;
if(unread_notification_count > 0)
@@ -523,7 +523,7 @@ namespace QuickMedia {
}
}
- update_room_description(room, messages, is_initial_sync);
+ update_room_description(room, messages, is_initial_sync, it.second.sync_is_cache);
}
pending_room_messages.clear();
}