aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-05-28 15:48:43 +0200
committerdec05eba <dec05eba@protonmail.com>2021-05-28 16:05:00 +0200
commit7b48ab891023d76de6b13b56a8d5e205f62bc37d (patch)
tree5863e129fd26bc2033d63b4f1ead25e25b83a4c2
parent2421bf3a24cd64767da1dde387c9caa27320dfc8 (diff)
Improve body navigation
-rw-r--r--TODO7
-rw-r--r--include/Body.hpp55
-rw-r--r--src/Body.cpp789
-rw-r--r--src/QuickMedia.cpp61
-rw-r--r--src/Text.cpp2
-rw-r--r--src/plugins/Matrix.cpp8
6 files changed, 340 insertions, 582 deletions
diff --git a/TODO b/TODO
index 2d03656..c103c0a 100644
--- a/TODO
+++ b/TODO
@@ -54,7 +54,7 @@ Modify sfml to use GL_COMPRESSED_LUMINANCE and other texture compression modes (
Decrease memory usage even further (mostly in matrix /sync when part of large rooms) by using rapidjson SAX style API to stream json string into SAX style parsing.
Sometimes we fail to get images in mangadex, most common reason being that the manga is licensed and we can't view the manga on mangadex. QuickMedia should implement mangaplus and redirect us to mangaplus plugin to view the manga, or simply show that we cant view the manga because its licensed.
Show redacted messages even when part of the initial sync in matrix. Right now they are hidden while new sync redacted messages are not.
-to fix this we could perhaps replace the newly created body items for replies when loading old messages and one of the old messages is also one of the embedded messages (by event id).
+ to fix this we could perhaps replace the newly created body items for replies when loading old messages and one of the old messages is also one of the embedded messages (by event id).
Add button to skip to next video. MPV has this feature when setting "next" video (can be done over IPC).
Use a custom allocator that replaces malloc/realloc/free/new/delete to release memory properly, using munmap in free/delete. The C allocator doesn't do that! memory usage remains high after one large allocation. The C allocator only marks it as free.
Merge |Page::search| and |Page::get_page|. get_page with page 0 should be the same as search.
@@ -130,4 +130,7 @@ Youtube now requires signing in to view age restricted content. For such videos
Notification race condition when fetching the first notifications page and receiving a notification immediately after the first sync? we might end up with a duplicate notification.
Submit on notifications item in matrix should jump to the message in the room.
Notifications should load their replied-to-message.
-Readd copying of Text in Body copy constructor. Find out why we need to make the text dirty on copy. \ No newline at end of file
+Readd copying of Text in Body copy constructor. Find out why we need to make the text dirty on copy.
+Readd touch scrolling to Body.
+Body items that are no longer visible should stop their thumbnail download/creation (moving to bottom of file-manager is very slow).
+Fix body flickering when moving up and there is a new local image to load. It happens because we have no idea how large the thumbnail is before loading it. \ No newline at end of file
diff --git a/include/Body.hpp b/include/Body.hpp
index 6977922..a74c320 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -144,7 +144,9 @@ namespace QuickMedia {
std::vector<size_t> replies;
std::string post_number;
void *userdata; // Not managed, should be deallocated by whoever sets this
- double last_drawn_time;
+ float prev_last_loaded_height = 0.0f;
+ float last_loaded_height = 0.0f;
+ float current_loaded_height = 0.0f;
FetchStatus embedded_item_status = FetchStatus::NONE;
// Important! Should refer to a new BodyItem, not one that already exists in the body.
// TODO: Allow referring to an existing body item. This doesn't work properly at the moment because max width of text and line count calculation getting messed up
@@ -154,6 +156,9 @@ namespace QuickMedia {
sf::Vector2i thumbnail_size;
std::vector<Reaction> reactions; // TODO: Move to a different body item type
std::shared_ptr<BodyItemExtra> extra; // TODO: Remove
+
+ // Internal use only
+ int keep_alive_frames = 0;
private:
// TODO: Clean up these strings when set in text, and get_title for example should return |title_text.getString()|
// TODO: Use sf::String instead, removes the need to convert to utf32 every time the text is dirty (for example when resizing window)
@@ -187,10 +192,10 @@ namespace QuickMedia {
bool select_next_page();
// Select previous item, ignoring invisible items. Returns true if the item was changed or if the item scrolled. This can be used to check if the top was hit when wrap_around is set to false
- bool select_previous_item(bool scroll_page_if_large_item = true);
+ bool select_previous_item(bool scroll_page_if_large_item = false);
// Select next item, ignoring invisible items. Returns true if the item was changed or if the item scrolled. This can be used to check if the bottom was hit when wrap_around is set to false
- bool select_next_item(bool scroll_page_if_large_item = true);
+ bool select_next_item(bool scroll_page_if_large_item = false);
void set_selected_item(int item, bool reset_prev_selected_item = true);
void reset_prev_selected_item();
@@ -198,11 +203,13 @@ namespace QuickMedia {
int get_index_by_body_item(BodyItem *body_item);
void select_first_item();
- void select_last_item(bool reset_prev_select = false);
+ void select_last_item();
void clear_items();
void prepend_items_reverse(BodyItems new_items);
void append_items(BodyItems new_items);
- void insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item);
+ // Returns the inserted position
+ size_t insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item);
+ // Note: also moves the selected item forward the number of items inserted before/after it (depending on attach_side)
void insert_items_by_timestamps(BodyItems new_items);
void clear_cache();
void clear_text_cache();
@@ -210,10 +217,6 @@ namespace QuickMedia {
BodyItem* get_selected() const;
std::shared_ptr<BodyItem> get_selected_shared();
- // Returns null if no visible items. This is the item we can see the end of
- BodyItem* get_last_fully_visible_item();
- BodyItem* get_last_visible_item();
-
void clamp_selection();
// Returns true if the event was handled
@@ -231,7 +234,7 @@ namespace QuickMedia {
float get_spacing_y() const;
// TODO: Highlight the part of the text that matches the search.
- void filter_search_fuzzy(const std::string &text, bool select_first_if_empty = true);
+ void filter_search_fuzzy(const std::string &text);
bool no_items_visible() const;
@@ -241,10 +244,9 @@ namespace QuickMedia {
void set_page_scroll(float scroll);
float get_page_scroll() const { return page_scroll; }
// This is the item we can see the start of
- bool is_first_item_fully_visible() const { return first_item_fully_visible; }
+ bool is_top_cut_off() const { return top_cut_off; }
// This is the item we can see the end of
- bool is_last_item_fully_visible() const { return last_item_fully_visible; }
- bool is_body_full_with_items() const { return items_cut_off; }
+ bool is_bottom_cut_off() const { return bottom_cut_off; }
int get_num_visible_items() const { return num_visible_items; };
// Call this once after adding new items
@@ -257,7 +259,6 @@ namespace QuickMedia {
sf::Text embedded_item_load_text;
BodyItems items;
bool draw_thumbnails;
- bool wrap_around;
// Set to {0, 0} to disable resizing
sf::Vector2i thumbnail_max_size;
sf::Color line_separator_color;
@@ -272,6 +273,7 @@ namespace QuickMedia {
std::function<void()> on_bottom_reached = nullptr;
private:
void filter_search_fuzzy_item(const std::string &text, BodyItem *body_item);
+ void handle_item_render(BodyItem *item, const sf::Vector2f pos, const sf::Vector2f size, const float item_height, int item_index);
void draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item = true, bool merge_with_previous = false);
void update_dirty_state(BodyItem *body_item, float width);
void clear_body_item_cache(BodyItem *body_item);
@@ -293,23 +295,25 @@ namespace QuickMedia {
APPLIED
};
+ enum class StuckDirection {
+ TOP,
+ NONE,
+ BOTTOM
+ };
+
Program *program;
int selected_item;
int prev_selected_item;
- double page_scroll;
- double prev_extra_scroll = 0.0;
- double extra_scroll_current = 0.0;
- double extra_scroll_target = 0.0;
+ double page_scroll = 0.0;
+ float selected_scrolled = 0.0f;
// 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;
sf::Sprite image;
sf::Sprite loading_icon;
int num_visible_items;
- bool first_item_fully_visible;
- bool last_item_fully_visible;
- int first_fully_visible_item;
- int last_fully_visible_item;
+ bool top_cut_off;
+ bool bottom_cut_off;
int first_visible_item = -1;
int last_visible_item = -1;
sf::Clock draw_timer;
@@ -317,15 +321,12 @@ namespace QuickMedia {
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;
- int clamp_selected_item_to_body_count = 1;
- int shit_hack_body_size_change = 0;
+ bool selected_item_fits_in_body = true;
bool mouse_left_pressed = false;
bool mouse_left_clicked = false;
bool has_scrolled_with_input = false;
bool click_counts = false;
+ StuckDirection stuck_direction = StuckDirection::NONE;
sf::Vector2f mouse_click_pos;
sf::Vector2f mouse_release_pos;
double mouse_press_pixels_moved_abs;
diff --git a/src/Body.cpp b/src/Body.cpp
index e219e58..8cc139c 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -27,6 +27,10 @@ static const float reaction_padding_y = std::floor(7.0f * QuickMedia::get_ui_sca
static const int embedded_item_font_size = std::floor(14 * QuickMedia::get_ui_scale());
namespace QuickMedia {
+ static float clamp(float value, float min, float max) {
+ return std::min(max, std::max(min, value));
+ }
+
BodyItem::BodyItem(std::string _title) :
visible(true),
dirty(false),
@@ -35,7 +39,6 @@ namespace QuickMedia {
dirty_timestamp(false),
thumbnail_is_local(false),
userdata(nullptr),
- last_drawn_time(0.0),
timestamp(0),
title_color(sf::Color::White),
author_color(sf::Color::White),
@@ -62,7 +65,9 @@ namespace QuickMedia {
replies = other.replies;
post_number = other.post_number;
userdata = other.userdata;
- last_drawn_time = other.last_drawn_time;
+ prev_last_loaded_height = other.prev_last_loaded_height;
+ last_loaded_height = other.last_loaded_height;
+ current_loaded_height = other.current_loaded_height;
embedded_item_status = other.embedded_item_status;
if(other.embedded_item) {
embedded_item.reset(new BodyItem(""));
@@ -87,6 +92,7 @@ namespace QuickMedia {
author_color = other.author_color;
description_color = other.description_color;
extra = other.extra;
+ keep_alive_frames = other.keep_alive_frames;
return *this;
}
@@ -103,20 +109,16 @@ namespace QuickMedia {
replies_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14 * get_ui_scale())),
embedded_item_load_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), embedded_item_font_size),
draw_thumbnails(true),
- wrap_around(false),
line_separator_color(sf::Color(32, 37, 43, 255)),
body_item_render_callback(nullptr),
thumbnail_mask_shader(nullptr),
program(program),
selected_item(0),
prev_selected_item(0),
- page_scroll(0.0f),
loading_icon(loading_icon_texture),
num_visible_items(0),
- first_item_fully_visible(true),
- last_item_fully_visible(true),
- first_fully_visible_item(-1),
- last_fully_visible_item(-1),
+ top_cut_off(false),
+ bottom_cut_off(false),
item_background(sf::Vector2f(1.0f, 1.0f), 10.0f, sf::Color(55, 60, 68), rounded_rectangle_shader),
reaction_background(sf::Vector2f(1.0f, 1.0f), 10.0f, sf::Color(33, 37, 44), rounded_rectangle_shader)
{
@@ -138,7 +140,7 @@ namespace QuickMedia {
// TODO: Make this work with wraparound enabled?
// TODO: For plugins with different sized body items this can be weird, because after scrolling down thumbnails could load and they could move items up/down until we see items we haven't seen
bool Body::select_previous_page() {
- if(!selected_line_top_visible)
+ if(!selected_item_fits_in_body)
return select_previous_item(false);
for(int i = 0; i < num_visible_items - 1; ++i) {
@@ -151,7 +153,7 @@ namespace QuickMedia {
// TODO: Make this work with wraparound enabled?
// TODO: For plugins with different sized body items this can be weird, because after scrolling down thumbnails could load and they could move items up/down until we see items we haven't seen
bool Body::select_next_page() {
- if(!selected_line_bottom_visible)
+ if(!selected_item_fits_in_body)
return select_next_item(false);
for(int i = 0; i < num_visible_items - 1; ++i) {
@@ -165,32 +167,13 @@ namespace QuickMedia {
if(items.empty())
return false;
- clamp_selected_item_to_body_count = 1;
-
- if(scroll_page_if_large_item && !selected_line_top_visible) {
- page_scroll += 128.0f;
+ if(scroll_page_if_large_item && !selected_item_fits_in_body && !selected_line_top_visible) {
+ selected_scrolled += 128.0f;
return true;
}
- int new_selected_item = selected_item;
- int num_items = (int)items.size();
-
- for(int i = 0; i < num_items; ++i) {
- if(new_selected_item - 1 < 0) {
- if(wrap_around)
- new_selected_item = num_items - 1;
- else {
- new_selected_item = selected_item;
- break;
- }
- } else {
- --new_selected_item;
- }
- if(items[new_selected_item]->visible)
- break;
- }
-
- if(selected_item == new_selected_item)
+ const int new_selected_item = get_previous_visible_item(selected_item);
+ if(new_selected_item == -1)
return false;
selected_item = new_selected_item;
@@ -201,32 +184,13 @@ namespace QuickMedia {
if(items.empty())
return false;
- clamp_selected_item_to_body_count = 1;
-
- if(scroll_page_if_large_item && !selected_line_bottom_visible) {
- page_scroll -= 128.0f;
+ if(scroll_page_if_large_item && !selected_item_fits_in_body && !selected_line_bottom_visible) {
+ selected_scrolled -= 128.0f;
return true;
}
- int new_selected_item = selected_item;
- int num_items = (int)items.size();
-
- for(int i = 0; i < num_items; ++i) {
- if(new_selected_item + 1 == num_items) {
- if(wrap_around) {
- new_selected_item = 0;
- } else {
- new_selected_item = selected_item;
- break;
- }
- } else {
- ++new_selected_item;
- }
- if(items[new_selected_item]->visible)
- break;
- }
-
- if(selected_item == new_selected_item)
+ const int new_selected_item = get_next_visible_item(selected_item);
+ if(new_selected_item == -1)
return false;
selected_item = new_selected_item;
@@ -237,9 +201,8 @@ namespace QuickMedia {
//assert(item >= 0 && item < (int)items.size());
selected_item = item;
clamp_selection();
- if(reset_prev_selected_item)
- prev_selected_item = selected_item;
- clamp_selected_item_to_body_count = 1;
+ //if(reset_prev_selected_item)
+ // prev_selected_item = selected_item;
//page_scroll = 0.0f;
}
@@ -257,26 +220,18 @@ namespace QuickMedia {
void Body::select_first_item() {
selected_item = 0;
- if(attach_side == AttachSide::TOP) {
- prev_selected_item = selected_item;
- page_scroll = 0.0f;
- }
+ //if(attach_side == AttachSide::TOP) {
+ // prev_selected_item = selected_item;
+ // page_scroll = 0.0f;
+ //}
clamp_selection();
- clamp_selected_item_to_body_count = 1;
- //item_background_target_pos_y = body_pos.y;
- //item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
}
- void Body::select_last_item(bool reset_prev_select) {
+ void Body::select_last_item() {
int new_selected_item = std::max(0, (int)items.size() - 1);
selected_item = new_selected_item;
- if(reset_prev_select)
- prev_selected_item = selected_item;
//page_scroll = 0.0f;
clamp_selection();
- clamp_selected_item_to_body_count = 1;
- //item_background_target_pos_y = body_pos.y + body_size.y - item_background.get_size().y;
- //item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
}
void Body::clear_items() {
@@ -284,9 +239,6 @@ namespace QuickMedia {
selected_item = 0;
prev_selected_item = selected_item;
page_scroll = 0.0f;
- clamp_selected_item_to_body_count = 1;
- //item_background_target_pos_y = body_pos.y;
- //item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
}
void Body::prepend_items_reverse(BodyItems new_items) {
@@ -300,22 +252,34 @@ namespace QuickMedia {
}
// TODO: Binary search and use hint to start search from start or end (for example when adding "previous" items or "next" items)
- void Body::insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item) {
+ size_t Body::insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item) {
for(size_t i = 0; i < items.size(); ++i) {
if(body_item->get_timestamp() < items[i]->get_timestamp()) {
items.insert(items.begin() + i, std::move(body_item));
- return;
+ return i;
}
}
items.push_back(std::move(body_item));
+ return items.size() - 1;
}
// TODO: Optimize by resizing |items| before insert
void Body::insert_items_by_timestamps(BodyItems new_items) {
+ int new_selected_item = selected_item;
+
for(auto &new_item : new_items) {
insert_item_by_timestamp(new_item);
+ if(attach_side == AttachSide::TOP) {
+ new_selected_item = get_previous_visible_item(new_selected_item);
+ } else {
+ new_selected_item = get_next_visible_item(new_selected_item);
+ }
}
+
+ selected_item = new_selected_item;
+ clamp_selection();
items_set_dirty();
+ prev_selected_item = selected_item;
}
void Body::clear_cache() {
@@ -343,18 +307,6 @@ namespace QuickMedia {
return items[selected_item];
}
- BodyItem* Body::get_last_fully_visible_item() {
- if(last_fully_visible_item < 0 || last_fully_visible_item >= (int)items.size() || !items[last_fully_visible_item]->visible)
- return nullptr;
- return items[last_fully_visible_item].get();
- }
-
- BodyItem* Body::get_last_visible_item() {
- if(last_visible_item < 0 || last_visible_item >= (int)items.size() || !items[last_visible_item]->visible)
- return nullptr;
- return items[last_visible_item].get();
- }
-
void Body::clamp_selection() {
int num_items = (int)items.size();
if(items.empty()) {
@@ -386,13 +338,13 @@ namespace QuickMedia {
if(keyboard_navigation && event.type == sf::Event::KeyPressed && !event.key.alt) {
if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) {
render_selected_item_bg = true;
- bool top_reached = select_previous_item();
+ bool top_reached = select_previous_item(true);
if(!top_reached && on_top_reached)
on_top_reached();
return true;
} else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) {
render_selected_item_bg = true;
- bool bottom_reached = select_next_item();
+ bool bottom_reached = select_next_item(true);
if(!bottom_reached && on_bottom_reached)
on_bottom_reached();
return true;
@@ -423,9 +375,6 @@ namespace QuickMedia {
}
}
- if(event.type == sf::Event::Resized)
- clamp_selected_item_to_body_count = 1;
-
if(!is_touch_enabled())
return false;
@@ -467,7 +416,7 @@ namespace QuickMedia {
void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress) {
if(items_dirty != DirtyState::FALSE) {
if(using_filter || items_dirty == DirtyState::FORCE_TRUE)
- filter_search_fuzzy(current_filter, false);
+ filter_search_fuzzy(current_filter);
items_dirty = DirtyState::FALSE;
}
@@ -475,467 +424,229 @@ namespace QuickMedia {
const float scissor_y = pos.y;
pos.y = 0.0f;
- const float start_y = pos.y;
float frame_time = frame_timer.restart().asSeconds();
- if(frame_time > 2.0f)
- frame_time = 2.0f;
+ if(frame_time > 1.0f)
+ frame_time = 1.0f;
if(selected_item >= (int)items.size())
selected_item = (int)items.size() - 1;
if(selected_item < 0)
- selected_item = 0;
+ selected_item = 0;
if(prev_selected_item < 0 || prev_selected_item >= (int)items.size()) {
prev_selected_item = selected_item;
}
bool body_size_changed = std::abs(size.x - body_size.x) > 0.1f || std::abs(size.y - body_size.y) > 0.1f;
- if(body_size_changed) {
+ if(body_size_changed)
body_size = size;
- shit_hack_body_size_change = 2;
- }
-
- if(shit_hack_body_size_change > 0)
- body_size_changed = true;
-
- shit_hack_body_size_change--;
- if(shit_hack_body_size_change < 0)
- shit_hack_body_size_change = 0;
elapsed_time_sec = draw_timer.getElapsedTime().asSeconds();
- const bool scroll_past_first = first_item_fully_visible && offset_to_top > 0.1f;
- const bool scroll_past_last = last_item_fully_visible && offset_to_bottom > 0.1f;
-
- if(std::abs(mouse_scroll_accel.y) > 0.1) {
- if((attach_side == AttachSide::BOTTOM && scroll_past_first) || (attach_side == AttachSide::TOP && scroll_past_last))
- body_size_changed = true;
- } else {
- if((attach_side == AttachSide::TOP && scroll_past_first) || (attach_side == AttachSide::BOTTOM && scroll_past_last))
- body_size_changed = true;
- }
-
- bool keep_selected_inside_body = clamp_selected_item_to_body_count > 0 || offset_to_top > 0.1f || offset_to_bottom > 0.1f;
- clamp_selected_item_to_body_count--;
- if(clamp_selected_item_to_body_count < 0)
- clamp_selected_item_to_body_count = 0;
-
- if(is_touch_enabled()) {
- const sf::Vector2f mouse_pos_diff(mouse_pos_raw.x - mouse_pos.x, mouse_pos_raw.y - mouse_pos.y);
- const float move_speed = 35.0f;
- sf::Vector2f prev_mouse_pos = mouse_pos;
- mouse_pos.x += (mouse_pos_diff.x * std::min(1.0f, frame_time * move_speed));
- mouse_pos.y += (mouse_pos_diff.y * std::min(1.0f, frame_time * move_speed));
- const sf::Vector2f mouse_pos_diff_smooth = mouse_pos - prev_mouse_pos;
-
- sf::Vector2f mouse_pos_raw_diff(mouse_pos_raw.x - prev_mouse_pos_raw.x, mouse_pos_raw.y - prev_mouse_pos_raw.y);
- prev_mouse_pos_raw = mouse_pos_raw;
-
- if(items_cut_off) {
- if(mouse_left_pressed) {
- page_scroll += mouse_pos_diff_smooth.y;
- mouse_scroll_accel = sf::Vector2f(mouse_pos_raw_diff.x, mouse_pos_raw_diff.y);
- mouse_scroll_accel *= (float)((double)frame_time * 120.0);
- } else {
- page_scroll += mouse_scroll_accel.y;
- }
- }
-
- if(mouse_scroll_accel.y > 0.1 && first_fully_visible_item == -1) {
- keep_selected_inside_body = true;
- } else if(mouse_scroll_accel.y < -0.1 && last_fully_visible_item == -1) {
- keep_selected_inside_body = true;
- }
-
- if(mouse_scroll_accel.y > 0.1f && first_fully_visible_item != -1) {
- selected_item = first_fully_visible_item;
- clamp_selection();
- // TODO: Cache this
- if(on_top_reached) {
- int first_visible_item_n = -1;
- for(int i = 0; i <= selected_item; ++i) {
- if(items[i]->visible) {
- first_visible_item_n = i;
- break;
- }
- }
- if(first_visible_item_n == first_fully_visible_item)
- on_top_reached();
- }
- } else if(mouse_scroll_accel.y < -0.1f && last_fully_visible_item != -1) {
- selected_item = last_fully_visible_item;
- clamp_selection();
- // TODO: Cache this
- if(on_bottom_reached) {
- int last_visible_item_n = -1;
- for(int i = items.size() - 1; i >= selected_item; --i) {
- if(items[i]->visible) {
- last_visible_item_n = i;
- break;
- }
- }
- if(last_visible_item_n == last_fully_visible_item)
- on_bottom_reached();
- }
- }
-
- if(!mouse_left_pressed) {
- const float scroll_deaccel = 1.02f;
- double deaccel = scroll_deaccel * (1.0 + frame_time);
- if(deaccel > 0.0001) {
- 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;
- } else {
- deaccel = 0.0;
- mouse_scroll_accel.x = 0.0;
- mouse_scroll_accel.y = 0.0;
- }
- }
- }
-
- const int prev_body_item_index = get_previous_visible_item(selected_item);
- bool selected_merge_with_previous = !items.empty() && body_item_merge_handler && body_item_merge_handler(prev_body_item_index == -1 ? nullptr : items[prev_body_item_index].get(), items[selected_item].get());
-
- double target_scroll = extra_scroll_target;
- if(selected_merge_with_previous)
- target_scroll += spacing_y;
- if(selected_item == last_visible_item)
- target_scroll -= selected_item_height;
- const double scroll_diff = target_scroll - extra_scroll_current;
- const double scroll_move_speed = (frame_time > 0.010f ? 10.0f : 15.0f);
- extra_scroll_current += (scroll_diff * std::min(1.0, frame_time * scroll_move_speed));
-
- double scroll_smooth_diff = extra_scroll_current - prev_extra_scroll;
- prev_extra_scroll = extra_scroll_current;
-
- if(body_size_changed) {
- extra_scroll_current = extra_scroll_target;
- prev_extra_scroll = extra_scroll_current;
- }
-
- bool selected_item_fits_on_screen = selected_item_height <= size.y;
- if(shit_hack_body_size_change == 0 && selected_item_fits_on_screen && render_selected_item_bg && !first_item_fully_visible && !last_item_fully_visible && items_cut_off && (selected_item == first_visible_item || selected_item == last_visible_item)) {
- page_scroll += scroll_smooth_diff;
- if(selected_item != first_fully_visible_item && selected_item != last_fully_visible_item)
- keep_selected_inside_body = false;
- }
-
- //item_separator.setFillColor(line_separator_color);
const int prev_num_visible_items = num_visible_items;
num_visible_items = 0;
- first_fully_visible_item = -1;
- last_fully_visible_item = -1;
first_visible_item = -1;
last_visible_item = -1;
selected_line_top_visible = true;
selected_line_bottom_visible = true;
+ selected_item_fits_in_body = true;
+ top_cut_off = false;
+ bottom_cut_off = false;
+
+ const int selected_item_diff = selected_item - prev_selected_item;
+ prev_selected_item = selected_item;
+
+ const int num_items = items.size();
+ if(num_items == 0 || size.x <= 0.001f || size.y <= 0.001f) {
+ if(num_items == 0)
+ page_scroll = 0.0f;
- int num_items = items.size();
- if(num_items == 0 || size.y <= 0.001f) {
for(auto &body_item : items) {
clear_body_item_cache(body_item.get());
if(body_item->embedded_item)
clear_body_item_cache(body_item->embedded_item.get());
}
- first_item_fully_visible = true;
- last_item_fully_visible = true;
- items_cut_off = false;
- offset_to_top = 0.0f;
- offset_to_bottom = 0.0f;
+
mouse_left_clicked = false;
- //item_background_target_pos_y = body_pos.y;
- //item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
return;
}
- // TODO: Optimize this, especially when scrolling to top/bottom.
- // TODO: If the thumbnail fallback size is larger than the real thumbnail size then the scroll will be incorrect when scrolling down
- // because first we scroll by a larger value and then the thumbnail size changes when the thumbnail has finished loading
- // and the selected item will no longer be exactly at the bottom of the window, and we will see parts of then next item.
- // To fix this, we should detect when the selected item is at the bottom of the screen and the thumbnail changes from image fallback to the real thumbnail
- // and add the difference in size to scroll, if the real thumbnail size is smaller than the image fallback.
- int selected_item_diff = selected_item - prev_selected_item;
- int selected_int_diff_abs = std::abs(selected_item_diff);
- if(selected_item_diff > 0) {
- int num_items_scrolled = 0;
- int i = prev_selected_item;
- const int prev_body_item_index = get_previous_visible_item(i);
- BodyItem *prev_body_item = prev_body_item_index == -1 ? nullptr : items[prev_body_item_index].get();
- double scroll_before = page_scroll;
- while(num_items_scrolled < selected_int_diff_abs && i < num_items) {
- if(items[i]->visible) {
- const bool merge_with_previous = body_item_merge_handler && body_item_merge_handler(prev_body_item, items[i].get());
- page_scroll += get_item_height(items[i].get(), size.x, selected_int_diff_abs < 50, true, merge_with_previous, i);
- if(merge_with_previous)
- page_scroll -= spacing_y;
- page_scroll += spacing_y;
- prev_body_item = items[i].get();
- }
- ++num_items_scrolled;
- ++i;
- }
- prev_selected_item = selected_item;
- if(selected_item_fits_on_screen)
- extra_scroll_target -= (page_scroll - scroll_before);
- } else if(selected_item_diff < 0) {
- int num_items_scrolled = 0;
- int i = prev_selected_item - 1;
- BodyItem *prev_body_item;
- double scroll_before = page_scroll;
- while(num_items_scrolled < selected_int_diff_abs && i >= 0) {
- if(items[i]->visible) {
- const int prev_body_item_index = get_previous_visible_item(i);
- prev_body_item = prev_body_item_index == -1 ? nullptr : items[prev_body_item_index].get();
- const bool merge_with_previous = body_item_merge_handler && body_item_merge_handler(prev_body_item, items[i].get());
- page_scroll -= get_item_height(items[i].get(), size.x, selected_int_diff_abs < 50, true, merge_with_previous, i);
- if(merge_with_previous)
- page_scroll += spacing_y;
- page_scroll -= spacing_y;
- }
- ++num_items_scrolled;
- --i;
- }
- prev_selected_item = selected_item;
- if(selected_item_fits_on_screen)
- extra_scroll_target -= (page_scroll - scroll_before);
- }
-
- if(keep_selected_inside_body) {
- if(!first_item_fully_visible && !last_item_fully_visible) {
- if(offset_to_top > 0.0f)
- page_scroll -= offset_to_top;
- else if(offset_to_bottom > 0.0f)
- page_scroll += offset_to_bottom;
- } else {
- if(attach_side == AttachSide::TOP) {
- if(first_item_fully_visible) {
- page_scroll -= offset_to_top;
- } else if(last_item_fully_visible && !first_item_fully_visible && body_size_changed) {
- page_scroll += offset_to_bottom;
- //page_scroll = get_offset_to_first_visible_item(size);
- }
- } else if(attach_side == AttachSide::BOTTOM) {
- if(last_item_fully_visible) {
- page_scroll += offset_to_bottom;
- } else if(first_item_fully_visible && !last_item_fully_visible && body_size_changed) {
- page_scroll -= offset_to_top;
- //page_scroll = get_offset_to_last_visible_item(size);
- }
- }
- }
- }
-
- selected_item_height = get_item_height(items[selected_item].get(), size.x, true, true, selected_merge_with_previous, selected_item);
- selected_item_height += spacing_y;
- 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;
-
- if(keep_selected_inside_body) {
- if(pos.y - start_y + page_scroll >= size.y && !selected_item_fits_on_screen)
- page_scroll = 0.0f;
- else if(pos.y - start_y + page_scroll + selected_item_height <= 0.0f && !selected_item_fits_on_screen)
- page_scroll = size.y - selected_item_height;
- }
-
- selected_line_top_visible |= selected_item_fits_on_screen;
- selected_line_bottom_visible |= selected_item_fits_on_screen;
-
- if(keep_selected_inside_body) {
- if(page_scroll > size.y - selected_item_height && selected_item_fits_on_screen) {
- page_scroll = size.y - selected_item_height;
- if(selected_merge_with_previous)
- page_scroll += spacing_y*2.0f;
-
- int next_body_item_index = get_next_visible_item(selected_item);
- const bool merge_with_next = next_body_item_index != -1 && body_item_merge_handler && body_item_merge_handler(items[selected_item].get(), items[next_body_item_index].get());
- if(!selected_merge_with_previous && merge_with_next)
- page_scroll += spacing_y;
- } else if(page_scroll < (selected_merge_with_previous ? spacing_y : 0.0f) && selected_line_top_visible && selected_item_fits_on_screen) {
- if(selected_merge_with_previous)
- page_scroll = spacing_y;
- else
- page_scroll = 0.0f;
- }
- }
-
- //page_scroll = std::floor(page_scroll);
- pos.y += page_scroll;
+ const int selected_prev_item = get_previous_visible_item(selected_item);
+ const bool selected_merge_with_previous = selected_prev_item != -1 && body_item_merge_handler && body_item_merge_handler(items[selected_prev_item].get(), items[selected_item].get());
+ get_item_height(items[selected_item].get(), size.x, true, true, selected_merge_with_previous, selected_item);
+ selected_item_fits_in_body = items[selected_item]->last_loaded_height < body_size.y;
+ if(selected_item_fits_in_body)
+ selected_scrolled = 0.0f;
- first_item_fully_visible = true;
- bool last_item_fully_visible_set = false;
- bool items_cut_off_set = false;
+ const sf::Vector2u window_size = window.getSize();
+ const sf::View prev_view = window.getView();
- sf::Vector2u window_size = window.getSize();
-
- sf::View prev_view = window.getView();
sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, size.y));
new_view.setViewport(sf::FloatRect(0.0f, scissor_y / (float)window_size.y, 1.0f, size.y / (float)window_size.y));
window.setView(new_view);
- if(prev_num_visible_items > 0) {
- bool instant_move = false;
- if(target_y_set == TargetSetState::SET) {
- target_y_set = TargetSetState::APPLIED;
- instant_move = true;
- }
-
- const float speed = frame_time > 0.010f ? 35.0f : 50.0f;
-
- const float item_background_prev_pos_y = item_background.get_position().y;
- const float item_background_pos_diff = item_background_target_pos_y - item_background_prev_pos_y;
- const float item_background_move_speed = instant_move ? 1000.0f : speed;
- item_background.set_position(sf::Vector2f(pos.x, item_background_prev_pos_y + (item_background_pos_diff * std::min(1.0f, frame_time * item_background_move_speed))));
-
- const float item_background_prev_height = item_background.get_size().y;
- const float item_background_height_diff = item_background_target_height - item_background_prev_height;
- const float item_background_height_speed = instant_move ? 1000.0f : speed;
- item_background.set_size(sf::Vector2f(size.x, item_background_prev_height + (item_background_height_diff * std::min(1.0f, frame_time * item_background_height_speed))));
-
- if(render_selected_item_bg)
- item_background.draw(window);
+ bool instant_move = body_size_changed;
+ if(target_y_set == TargetSetState::SET) {
+ target_y_set = TargetSetState::APPLIED;
+ instant_move = true;
}
- sf::Vector2f prev_pos = pos;
- int i;
- for(i = selected_item - 1; i >= 0;) {
- auto &item = items[i];
-
- // TODO: Find a better solution?
- if(!item->visible) {
- --i;
- continue;
- }
-
- int prev_body_item_index = get_previous_visible_item(i);
- const bool merge_with_previous = body_item_merge_handler && body_item_merge_handler(prev_body_item_index == -1 ? nullptr : items[prev_body_item_index].get(), item.get());
+ const float speed = 30.0f;
- item->last_drawn_time = elapsed_time_sec;
- float item_height = get_item_height(item.get(), size.x, true, true, merge_with_previous, i);
- float item_height_with_merge = item_height + spacing_y;
- prev_pos.y -= item_height_with_merge;
+ const float item_background_prev_height = item_background.get_size().y;
+ const float item_background_height_diff = item_background_target_height - item_background_prev_height;
+ const float item_background_height_speed = instant_move ? 1000.0f : speed;
+ const float item_background_new_height = item_background_prev_height + (item_background_height_diff * std::min(1.0f, frame_time * item_background_height_speed));
+ item_background.set_size(sf::Vector2f(size.x, item_background_new_height));
- if(prev_pos.y < start_y) {
- items_cut_off = true;
- items_cut_off_set = true;
- first_item_fully_visible = false;
- }
-
- if(prev_pos.y + item_height_with_merge <= start_y)
- break;
-
- draw_item(window, item.get(), prev_pos, size, item_height, i, content_progress, true, merge_with_previous);
- ++num_visible_items;
- first_visible_item = i;
- last_visible_item = i;
-
- if(first_item_fully_visible)
- first_fully_visible_item = i;
+ const float item_background_prev_pos_y = item_background.get_position().y;
+ const float item_background_pos_diff = item_background_target_pos_y - item_background_prev_pos_y;
+ const float item_background_move_speed = instant_move ? 1000.0f : speed;
+ float item_background_new_pos_y = item_background_prev_pos_y + (item_background_pos_diff * std::min(1.0f, frame_time * item_background_move_speed));
+ if(selected_item_fits_in_body) {
+ item_background_new_pos_y = std::min(item_background_new_pos_y, body_size.y - item_background_new_height - spacing_y);
+ item_background_new_pos_y = std::max(item_background_new_pos_y, 0.0f);
+ }
+ item_background.set_position(sf::Vector2f(pos.x, item_background_new_pos_y));
- if(merge_with_previous)
- prev_pos.y += spacing_y;
+ if(prev_num_visible_items > 0 && render_selected_item_bg)
+ item_background.draw(window);
+
+ int index;
+ if(attach_side == AttachSide::TOP) {
+ if(page_scroll > 0.0)
+ page_scroll = 0.0;
+ pos.y += page_scroll;
+ index = get_next_visible_item(-1);
- i = prev_body_item_index;
+ if(pos.y + selected_scrolled > 0.0f)
+ selected_scrolled = 0.0f;
+ else
+ pos.y += selected_scrolled;
+ } else {
+ if(page_scroll < 0.0)
+ page_scroll = 0.0;
+ pos.y += body_size.y;
+ pos.y += page_scroll;
+ index = get_previous_visible_item(num_items);
+
+ if(pos.y + selected_scrolled < body_size.y)
+ selected_scrolled = 0.0f;
+ else
+ pos.y += selected_scrolled;
}
- offset_to_top = prev_pos.y - start_y;
+ pos.y += selected_scrolled;
- BodyItem *prev_body_item = prev_body_item_index == -1 ? nullptr : items[prev_body_item_index].get();
- bool after_merge_with_previous = false;
+ BodyItem *prev_body_item = nullptr;
+ const double height_move_speed = 1000.0f;
- sf::Vector2f after_pos = pos;
- for(int i = selected_item; i < num_items;) {
- auto &item = items[i];
+ // TODO: Improve performance. Skip items that are obviously not visible or anywhere near the body. Limit loop to 100-200 items around the selected item
+ while(index != -1) {
+ BodyItem *item = items[index].get();
+ assert(item->visible);
- // TODO: Find a better solution?
- if(!item->visible) {
- ++i;
- continue;
+ int prev_index;
+ if(attach_side == AttachSide::BOTTOM) {
+ prev_index = get_previous_visible_item(index);
+ if(prev_index == -1)
+ prev_body_item = nullptr;
+ else
+ prev_body_item = items[prev_index].get();
}
- after_merge_with_previous = body_item_merge_handler && body_item_merge_handler(prev_body_item, item.get());
- if(after_merge_with_previous)
- after_pos.y -= spacing_y;
+ const bool merge_with_previous = body_item_merge_handler && body_item_merge_handler(prev_body_item, item);
+ if(attach_side == AttachSide::TOP && merge_with_previous)
+ pos.y -= spacing_y;
+
+ get_item_height(item, size.x, false, true, merge_with_previous, index);
+ const float item_height_diff = item->last_loaded_height - item->current_loaded_height;
+ const float add_height = item_height_diff * std::min(1.0, frame_time * height_move_speed);
+ item->current_loaded_height += add_height;
+ //page_scroll += add_height;
- float item_height = get_item_height(item.get(), size.x, true, true, after_merge_with_previous, i);
+ float top_y;
+ if(attach_side == AttachSide::TOP)
+ top_y = pos.y;
+ else
+ top_y = pos.y - (item->current_loaded_height + spacing_y);
- if(after_pos.y < start_y) {
- items_cut_off = true;
- first_item_fully_visible = false;
+ if(top_y < 0.0f) {
+ top_cut_off = true;
+ if(index == selected_item)
+ selected_line_top_visible = false;
}
- if(after_pos.y - start_y >= size.y) {
- if(first_fully_visible_item == -1)
- first_item_fully_visible = false;
- last_item_fully_visible = false;
- items_cut_off = true;
- last_item_fully_visible_set = true;
- items_cut_off_set = true;
- break;
+ if(top_y + item->current_loaded_height > body_size.y) {
+ bottom_cut_off = true;
+ if(index == selected_item)
+ selected_line_bottom_visible = false;
}
- item->last_drawn_time = elapsed_time_sec;
+ const bool is_item_visible_in_body = top_y + item->current_loaded_height >= 0.0f && top_y <= body_size.y;
+ if(is_item_visible_in_body || index == selected_item) {
+ get_item_height(item, size.x, true, true, merge_with_previous, index);
+ const float item_height_diff = item->last_loaded_height - item->current_loaded_height;
+ const float add_height = item_height_diff * std::min(1.0, frame_time * height_move_speed);
+ item->current_loaded_height += add_height;
+ if(attach_side == AttachSide::BOTTOM)
+ pos.y -= (item->current_loaded_height + spacing_y);
+ //page_scroll += add_height;
+
+ //const float top_y_clamped = clamp(pos.y, 0.0f, body_size.y);
+ //const float bottom_y_clamped = std::min(pos.y + item->current_loaded_height, body_size.y);
+
+ //float offset_y = 0.0f;
+ //if(pos.y < 0.0f)
+ // offset_y = pos.y;
+ //else if(pos.y > body_size.y)
+ // offset_y = body_size.y - pos.y;
+
+ //sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, scissor_y + bottom_y_clamped));
+ //new_view.setViewport(sf::FloatRect(0.0f, (scissor_y + top_y_clamped) / (float)window_size.y, 1.0f, (scissor_y + bottom_y_clamped) / (float)window_size.y));
+ //window.setView(new_view);
+
+ draw_item(window, item, pos/*sf::Vector2f(pos.x, offset_y)*/, size, item->current_loaded_height, index, content_progress, true, merge_with_previous);
+ handle_item_render(item, pos, size, item->current_loaded_height, index);
+ ++num_visible_items;
+
+ if(first_visible_item == -1 || index < first_visible_item)
+ first_visible_item = index;
+ if(last_visible_item == -1 || index > last_visible_item)
+ last_visible_item = index;
+
+ if(attach_side == AttachSide::TOP)
+ pos.y += (item->current_loaded_height + spacing_y);
+ } else {
+ if(attach_side == AttachSide::TOP)
+ pos.y += (item->current_loaded_height + spacing_y);
+ else
+ pos.y -= (item->current_loaded_height + spacing_y);
- draw_item(window, item.get(), after_pos, size, item_height, i, content_progress, true, after_merge_with_previous);
- after_pos.y += item_height;
- after_pos.y += spacing_y;
- ++num_visible_items;
- if(first_visible_item == -1)
- first_visible_item = i;
- last_visible_item = i;
+ if(item->keep_alive_frames == 0) {
+ clear_body_item_cache(item);
+ // TODO: Make sure the embedded item is not referencing another item in the |items| list
+ if(item->embedded_item)
+ clear_body_item_cache(item->embedded_item.get());
+ } else {
+ --item->keep_alive_frames;
+ }
+ }
- int next_body_item_index = get_next_visible_item(i);
- const bool merge_with_next = next_body_item_index != -1 && body_item_merge_handler && body_item_merge_handler(item.get(), next_body_item_index == -1 ? nullptr : items[next_body_item_index].get());
+ if(attach_side == AttachSide::BOTTOM && merge_with_previous)
+ pos.y += spacing_y;
- if(after_pos.y - start_y - (merge_with_next ? 0.0f : spacing_y) > size.y) {
- last_item_fully_visible = false;
- items_cut_off = true;
- last_item_fully_visible_set = true;
- items_cut_off_set = true;
+ if(attach_side == AttachSide::TOP) {
+ prev_body_item = item;
+ index = get_next_visible_item(index);
} else {
- last_fully_visible_item = i;
+ index = prev_index;
}
-
- if(first_item_fully_visible && first_fully_visible_item == -1)
- first_fully_visible_item = i;
-
- prev_body_item = items[i].get();
-
- i = next_body_item_index;
- if(i == -1)
- break;
}
window.setView(prev_view);
- if(first_fully_visible_item == -1)
- first_fully_visible_item = selected_item;
- if(last_fully_visible_item == -1)
- last_fully_visible_item = selected_item;
-
- offset_to_bottom = size.y - (after_pos.y - start_y - (last_item_fully_visible && after_merge_with_previous ? spacing_y : 0.0f));
-
- if(!last_item_fully_visible_set)
- last_item_fully_visible = true;
- if(!items_cut_off_set)
- items_cut_off = false;
-
- // TODO: Only do this for items that are not visible, do not loop all items.
- // TODO: Improve performance! right now it can use up to 5-7% cpu with a lot of items!
- for(auto &body_item : items) {
- if(elapsed_time_sec - body_item->last_drawn_time >= 1.5)
- clear_body_item_cache(body_item.get());
- // The embedded item might or might not refer to another item in |items|, so we have to make sure we also check it
- if(body_item->embedded_item && elapsed_time_sec - body_item->embedded_item->last_drawn_time >= 1.5)
- clear_body_item_cache(body_item->embedded_item.get());
- }
-
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
@@ -943,6 +654,37 @@ namespace QuickMedia {
if(body_item_select_callback)
body_item_select_callback(clicked_body_item_tmp.get());
}
+
+ const float item_target_top_diff = item_background_target_pos_y;
+ const float item_target_bottom_diff = (item_background_target_pos_y + item_background_target_height + spacing_y) - body_size.y;
+ if(((body_size_changed && attach_side == AttachSide::BOTTOM) || selected_item_diff < 0) && item_target_top_diff < 0.0f) {
+ //extra_scroll_target -= item_target_top_diff;
+ stuck_direction = StuckDirection::TOP;
+ } else if(((body_size_changed && attach_side == AttachSide::TOP) || selected_item_diff > 0) && item_target_bottom_diff > 0.0) {
+ //extra_scroll_target -= item_target_bottom_diff;
+ stuck_direction = StuckDirection::BOTTOM;
+ }
+
+ if(stuck_direction == StuckDirection::TOP && selected_item_diff > 0 && item_target_top_diff > -0.0001f/* && scroll_diff > -0.0001f*/)
+ stuck_direction = StuckDirection::NONE;
+
+ if(stuck_direction == StuckDirection::BOTTOM && selected_item_diff < 0 && item_target_bottom_diff < 0.0001f/* && scroll_diff < 0.0001f*/)
+ stuck_direction = StuckDirection::NONE;
+
+ const float page_scroll_speed = std::min(1.0f, frame_time * speed);
+ if(stuck_direction == StuckDirection::TOP) {
+ if(body_size_changed)
+ page_scroll -= item_target_top_diff;
+ else
+ page_scroll -= item_target_top_diff*page_scroll_speed;
+ }
+
+ if(stuck_direction == StuckDirection::BOTTOM) {
+ if(body_size_changed)
+ page_scroll -= item_target_bottom_diff;
+ else
+ page_scroll -= item_target_bottom_diff*page_scroll_speed;
+ }
}
void Body::update_dirty_state(BodyItem *body_item, float width) {
@@ -1029,6 +771,8 @@ namespace QuickMedia {
body_item->timestamp_text.reset();
body_item->dirty_timestamp = true;
}
+
+ body_item->keep_alive_frames = 0;
}
sf::Vector2i Body::get_item_thumbnail_size(BodyItem *item) const {
@@ -1064,7 +808,7 @@ namespace QuickMedia {
}
void Body::draw_item(sf::RenderWindow &window, BodyItem *item, sf::Vector2f pos, sf::Vector2f size, bool include_embedded_item, bool is_embedded) {
- item->last_drawn_time = draw_timer.getElapsedTime().asMilliseconds();
+ item->keep_alive_frames = 3;
get_item_height(item, size.x, true, false, false, -1);
draw_item(window, item, pos, size, size.y + spacing_y, -1, Json::Value::nullSingleton(), include_embedded_item);
}
@@ -1085,6 +829,23 @@ namespace QuickMedia {
return "";
}
+ void Body::handle_item_render(BodyItem *item, const sf::Vector2f pos, const sf::Vector2f size, const float item_height, int item_index) {
+ if(body_item_select_callback && mouse_left_clicked && !clicked_body_item && click_counts && std::abs(mouse_scroll_accel.y) < 5.0f) {
+ sf::FloatRect item_box(pos + body_pos, sf::Vector2f(size.x, item_height));
+ if(item_box.contains(mouse_click_pos) && item_box.contains(mouse_release_pos) && mouse_press_pixels_moved_abs <= 25.0) {
+ clicked_body_item = items[item_index];
+ set_selected_item(item_index, false);
+ }
+ }
+
+ if(item_index == selected_item) {
+ item_background_target_pos_y = pos.y;
+ item_background_target_height = item_height;
+ if(target_y_set == TargetSetState::NOT_SET)
+ target_y_set = TargetSetState::SET;
+ }
+ }
+
void Body::draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item, bool merge_with_previous) {
sf::Vector2i thumbnail_size = get_item_thumbnail_size(item);
std::shared_ptr<ThumbnailData> item_thumbnail;
@@ -1100,25 +861,6 @@ namespace QuickMedia {
item_pos.x = std::floor(pos.x);
item_pos.y = std::floor(pos.y);
- if(body_item_select_callback && mouse_left_clicked && !clicked_body_item && click_counts && std::abs(mouse_scroll_accel.y) < 5.0f) {
- sf::FloatRect item_box(pos + body_pos, sf::Vector2f(size.x, item_height));
- if(item_box.contains(mouse_click_pos) && item_box.contains(mouse_release_pos) && mouse_press_pixels_moved_abs <= 25.0) {
- clicked_body_item = items[item_index];
- set_selected_item(item_index, false);
- }
- }
-
- //item_separator.setSize(sf::Vector2f(std::max(0.0f, size.x - 20.0f), 1.0f));
- //item_separator.setPosition(item_pos + sf::Vector2f(10.0f, std::floor(item_height + spacing_y * 0.5f)));
- //window.draw(item_separator);
-
- if(item_index == selected_item) {
- item_background_target_pos_y = item_pos.y;
- item_background_target_height = item_height;
- if(target_y_set == TargetSetState::NOT_SET)
- target_y_set = TargetSetState::SET;
- }
-
float text_offset_x = padding_x;
if(item_thumbnail && !merge_with_previous) {
// TODO: Verify if this is safe. The thumbnail is being modified in another thread
@@ -1281,9 +1023,6 @@ namespace QuickMedia {
}
float Body::get_item_height(BodyItem *item, float width, bool load_texture, bool include_embedded_item, bool merge_with_previous, int item_index) {
- if(load_texture)
- item->last_drawn_time = elapsed_time_sec;
-
sf::Vector2i content_size = get_item_thumbnail_size(item);
float image_height = 0.0f;
@@ -1357,6 +1096,22 @@ namespace QuickMedia {
item_height = std::max(item_height, image_height);
item_height += (padding_y * 2.0f);
+ const bool first_height_set = item->last_loaded_height < 0.01f;
+ if(item_index != -1 && (first_height_set || load_texture)) {
+ const float height_diff = item_height - item->prev_last_loaded_height;
+ //if(attach_side == AttachSide::TOP) {
+ // if(item_index < selected_item)
+ // extra_scroll_target -= height_diff;
+ //} else if(attach_side == AttachSide::BOTTOM) {
+ // if(item_index > selected_item)
+ // extra_scroll_target += height_diff;
+ //}
+ item->last_loaded_height = item_height;
+ item->prev_last_loaded_height = item_height;
+ if(first_height_set)
+ item->current_loaded_height = item_height;
+ }
+
return item_height;
}
@@ -1388,16 +1143,13 @@ namespace QuickMedia {
return full_match;
}
- void Body::filter_search_fuzzy(const std::string &text, bool select_first_if_empty) {
+ void Body::filter_search_fuzzy(const std::string &text) {
current_filter = text;
if(text.empty()) {
for(auto &item : items) {
item->visible = true;
}
-
- if(select_first_if_empty)
- select_first_item();
return;
}
@@ -1405,7 +1157,7 @@ namespace QuickMedia {
filter_search_fuzzy_item(text, item.get());
}
- select_first_item();
+ clamp_selection();
using_filter = true;
}
@@ -1437,7 +1189,6 @@ namespace QuickMedia {
void Body::set_page_scroll(float scroll) {
page_scroll = scroll;
- clamp_selected_item_to_body_count = 1;
}
void Body::items_set_dirty(bool force) {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 0c2e872..7dbccae 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -648,8 +648,8 @@ namespace QuickMedia {
window.setVerticalSyncEnabled(true);
monitor_hz = get_monitor_max_hz(disp);
- window.setFramerateLimit(FPS_IDLE);
- idle = true;
+ window.setFramerateLimit(monitor_hz);
+ idle = false;
vsync_set = false;
/*
if(enable_vsync(disp, window.getSystemHandle())) {
@@ -1537,9 +1537,8 @@ namespace QuickMedia {
return;
}
- window.setFramerateLimit(FPS_IDLE);
malloc_trim(0);
- idle = true;
+ idle_active_handler();
bool loop_running = true;
bool redraw = true;
@@ -1615,6 +1614,8 @@ namespace QuickMedia {
return;
}
+ idle_active_handler();
+
if(tabs[selected_tab].page->clear_search_after_submit() && tabs[selected_tab].search_bar) {
if(!tabs[selected_tab].search_bar->get_text().empty()) {
tabs[selected_tab].search_bar->clear();
@@ -1660,7 +1661,7 @@ namespace QuickMedia {
select_episode(selected_item.get(), false);
Body *chapters_body = tabs[selected_tab].body.get();
tabs[selected_tab].search_bar->clear();
- chapters_body->filter_search_fuzzy("", false); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter
+ chapters_body->filter_search_fuzzy(""); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter
MangaImagesPage *manga_images_page = static_cast<MangaImagesPage*>(new_tabs[0].page.get());
window.setKeyRepeatEnabled(false);
downloading_chapter_url.clear();
@@ -1983,9 +1984,12 @@ namespace QuickMedia {
if(body_was_empty) {
tabs[i].body->select_last_item();
} else {
+ // TODO: Use select_next_item in a loop instead for |num_new_messages|?
tabs[i].body->set_selected_item(prev_selected_item + num_new_messages, true);
}
}
+
+ idle_active_handler();
}
if(associated_data.search_text_updated && associated_data.fetch_status == FetchStatus::NONE && !associated_data.fetching_next_page_running) {
@@ -2020,6 +2024,7 @@ namespace QuickMedia {
associated_data.search_result_text.setString("No results found");
else
associated_data.search_result_text.setString("");
+ idle_active_handler();
} else {
associated_data.fetch_future.get();
}
@@ -2031,6 +2036,9 @@ namespace QuickMedia {
FetchResult fetch_result = associated_data.fetch_future.get();
tabs[i].body->items = std::move(fetch_result.body_items);
if(tabs[i].search_bar) tabs[i].body->filter_search_fuzzy(tabs[i].search_bar->get_text());
+ if(tabs[i].body->attach_side == AttachSide::TOP) {
+ tabs[i].body->select_first_item();
+ }
if(tabs[i].body->attach_side == AttachSide::BOTTOM) {
std::reverse(tabs[i].body->items.begin(), tabs[i].body->items.end());
tabs[i].body->select_last_item();
@@ -2043,6 +2051,7 @@ namespace QuickMedia {
else
associated_data.search_result_text.setString("");
associated_data.fetch_status = FetchStatus::NONE;
+ idle_active_handler();
}
}
@@ -2071,9 +2080,9 @@ namespace QuickMedia {
window.display();
if(!tabs[selected_tab].body->items.empty()) {
- if(tabs[selected_tab].body->attach_side == AttachSide::TOP && tabs[selected_tab].body->is_last_item_fully_visible())
+ if(tabs[selected_tab].body->attach_side == AttachSide::TOP && !tabs[selected_tab].body->is_bottom_cut_off())
on_reached_end();
- else if(tabs[selected_tab].body->attach_side == AttachSide::BOTTOM && tabs[selected_tab].body->is_first_item_fully_visible())
+ else if(tabs[selected_tab].body->attach_side == AttachSide::BOTTOM && !tabs[selected_tab].body->is_top_cut_off())
on_reached_end();
}
@@ -2222,6 +2231,8 @@ namespace QuickMedia {
TaskResult Program::run_task_with_loading_screen(std::function<bool()> callback) {
assert(std::this_thread::get_id() == main_thread_id);
+ idle_active_handler();
+
AsyncTask<bool> task = callback;
window_size.x = window.getSize().x;
@@ -4456,8 +4467,8 @@ namespace QuickMedia {
}
};
- 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]() {
+ auto upload_file = [this, &current_room](const std::string &filepath) {
+ run_task_with_loading_screen([this, &current_room, filepath]() {
std::string event_id_response;
std::string err_msg;
if(matrix->post_file(current_room, filepath, event_id_response, err_msg) == PluginResult::OK) {
@@ -4467,11 +4478,6 @@ namespace QuickMedia {
return false;
}
});
-
- if(post_file_result == TaskResult::TRUE) {
- if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible())
- tabs[MESSAGES_TAB_INDEX].body->select_last_item();
- }
};
struct Mention {
@@ -4490,6 +4496,7 @@ namespace QuickMedia {
filter_updated = false;
filter.clear();
users_tab_body->filter_search_fuzzy("");
+ users_tab_body->select_first_item();
users_tab_body->clear_cache();
}
@@ -4505,9 +4512,9 @@ namespace QuickMedia {
}
} else if(event.type == sf::Event::KeyPressed) {
if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) {
- users_tab_body->select_previous_item();
+ users_tab_body->select_previous_item(true);
} else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) {
- users_tab_body->select_next_item();
+ users_tab_body->select_next_item(true);
} else if(event.key.code == sf::Keyboard::Enter && event.key.shift) {
hide();
} else if(event.key.code == sf::Keyboard::Backspace) {
@@ -4531,6 +4538,7 @@ namespace QuickMedia {
// TODO: Use std::string instead of sf::String
auto u8 = filter.toUtf8();
users_tab_body->filter_search_fuzzy(*(std::string*)&u8);
+ users_tab_body->select_first_item();
}
}
};
@@ -4954,17 +4962,11 @@ namespace QuickMedia {
scroll_to_end = true;
}
- BodyItem *selected_item = tabs[MESSAGES_TAB_INDEX].body->get_selected();
auto new_body_items = messages_to_body_items(current_room, messages, current_room->get_user_display_name(me), me->user_id);
messages_load_cached_related_embedded_item(new_body_items, tabs[MESSAGES_TAB_INDEX].body->items, me, current_room);
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(std::move(new_body_items));
- if(selected_item && !scroll_to_end) {
- int selected_item_index = tabs[MESSAGES_TAB_INDEX].body->get_index_by_body_item(selected_item);
- if(selected_item_index != -1)
- tabs[MESSAGES_TAB_INDEX].body->set_selected_item(selected_item_index);
- } else if(scroll_to_end) {
+ if(scroll_to_end)
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
- }
};
auto display_url_or_image = [this, matrix_chat_page, &ui_tabs, &redraw, &video_page, &launch_url, &chat_state, &url_selection_body, &avatar_applied, PINNED_TAB_INDEX, MESSAGES_TAB_INDEX](BodyItem *selected) {
@@ -5201,11 +5203,11 @@ namespace QuickMedia {
if(event.type == sf::Event::KeyPressed && event.key.control && event.key.alt && (chat_state == ChatState::NAVIGATING || chat_state == ChatState::URL_SELECTION)) {
if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) {
- matrix_chat_page->rooms_page->body->select_previous_item();
+ matrix_chat_page->rooms_page->body->select_previous_item(true);
move_room = true;
goto chat_page_end;
} else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) {
- matrix_chat_page->rooms_page->body->select_next_item();
+ matrix_chat_page->rooms_page->body->select_next_item(true);
move_room = true;
goto chat_page_end;
} else if(event.key.code == sf::Keyboard::PageUp) {
@@ -5487,6 +5489,7 @@ namespace QuickMedia {
upload_file(selected_files[0]);
}
redraw = true;
+ avatar_applied = false;
break;
}
case PageType::CHAT_LOGIN: {
@@ -5808,7 +5811,7 @@ namespace QuickMedia {
}
if(selected_tab == MESSAGES_TAB_INDEX && current_room && current_room->body_item && !current_room->last_message_read && matrix->is_initial_sync_finished()) {
- if(last_visible_timeline_message && tabs[selected_tab].body->is_last_item_fully_visible() && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) {
+ if(last_visible_timeline_message && !tabs[selected_tab].body->is_bottom_cut_off() && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) {
std::string room_desc = current_room->body_item->get_description();
if(strncmp(room_desc.c_str(), "Unread: ", 8) == 0)
room_desc = room_desc.substr(8);
@@ -5847,7 +5850,7 @@ namespace QuickMedia {
}
});
}
- } else if(!tabs[selected_tab].body->is_last_item_fully_visible()) {
+ } else if(tabs[selected_tab].body->is_bottom_cut_off()) {
window.draw(more_messages_below_rect);
}
}
@@ -5926,8 +5929,7 @@ namespace QuickMedia {
process_reactions(all_messages_new);
if(current_room->initial_prev_messages_fetch) {
current_room->initial_prev_messages_fetch = false;
- if(selected_tab == MESSAGES_TAB_INDEX)
- tabs[MESSAGES_TAB_INDEX].body->select_last_item();
+ tabs[MESSAGES_TAB_INDEX].body->select_last_item();
}
std::vector<std::string> pinned_events;
@@ -6583,6 +6585,7 @@ namespace QuickMedia {
}
ui_tabs.set_text(tab_path_index, file_manager_page->get_current_directory().string());
+ idle_active_handler();
};
const float bottom_panel_padding = 10.0f;
diff --git a/src/Text.cpp b/src/Text.cpp
index 0834048..2e735ab 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -379,7 +379,7 @@ namespace QuickMedia
sf::Vector2f glyphPos;
sf::Uint32 prevCodePoint = 0;
- // TODO: Only do this if dirtyText
+ // TODO: Only do this if dirtyText (then the Text object shouldn't be reset in Body. There should be a cleanup function in text instead)
for(usize textElementIndex = 0; textElementIndex < textElements.size(); ++textElementIndex)
{
TextElement &textElement = textElements[textElementIndex];
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index de50049..552762a 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -614,7 +614,7 @@ namespace QuickMedia {
bool is_window_focused = program->is_window_focused();
RoomData *current_room = program->get_current_chat_room();
Body *chat_body = chat_page ? chat_page->chat_body : nullptr;
- bool set_room_as_unread = !is_window_focused || room != current_room || (!chat_body || !chat_body->is_last_item_fully_visible()) || (chat_page && !chat_page->messages_tab_visible);
+ bool set_room_as_unread = !is_window_focused || room != current_room || (!chat_body || chat_body->is_bottom_cut_off()) || (chat_page && !chat_page->messages_tab_visible);
std::string room_desc;
if(set_room_as_unread)
@@ -692,7 +692,7 @@ namespace QuickMedia {
body->items.insert(body->items.begin() + (i - 1), std::move(body_item_to_insert));
if((int)i < selected_item && room_body_index > selected_item && body->items.size() > 1 && i != body->items.size() - 1) {
body->select_first_item();
- body->set_selected_item(selected_item + 1, false);
+ body->select_next_item();
}
return;
}
@@ -719,6 +719,7 @@ namespace QuickMedia {
void MatrixRoomsPage::clear_search() {
search_bar->clear();
body->filter_search_fuzzy("");
+ body->select_first_item();
}
void MatrixRoomsPage::clear_data() {
@@ -1107,9 +1108,8 @@ namespace QuickMedia {
//int prev_selected_item = notifications_body->get_selected_item();
//notifications_body->items.push_back(notification_to_body_item(notifications_body, notification));
//notifications_body->set_selected_item(prev_selected_item - 1);
- int prev_selected_item = notifications_body->get_selected_item();
notifications_body->items.insert(notifications_body->items.begin(), notification_to_body_item(notifications_body, notification));
- notifications_body->set_selected_item(prev_selected_item + 1, false);
+ notifications_body->select_next_item();
}
void MatrixNotificationsPage::set_room_as_read(RoomData *room) {