aboutsummaryrefslogtreecommitdiff
path: root/src/Body.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Body.cpp')
-rw-r--r--src/Body.cpp789
1 files changed, 270 insertions, 519 deletions
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) {