aboutsummaryrefslogtreecommitdiff
path: root/src/Body.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Body.cpp')
-rw-r--r--src/Body.cpp699
1 files changed, 480 insertions, 219 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index ffd9337..c0d2a74 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -29,6 +29,16 @@ struct BodySpacing {
float embedded_item_font_size = 0.0f;
};
+static const int card_width = 250.0f * QuickMedia::get_ui_scale();
+static const int card_height = 350.0f * QuickMedia::get_ui_scale();
+
+static const int min_column_spacing = 10 * QuickMedia::get_ui_scale();
+static const int card_padding_x = 20 * QuickMedia::get_ui_scale();
+static const int card_padding_y = 20 * QuickMedia::get_ui_scale();
+static const int card_image_text_padding = 10 * QuickMedia::get_ui_scale();
+static const sf::Vector2i card_max_image_size(card_width - card_padding_x * 2, (card_height - card_padding_y * 2) / 2);
+static const int num_columns_switch_to_list = 1;
+
static BodySpacing body_spacing[2];
static bool themes_initialized = false;
@@ -76,6 +86,10 @@ namespace QuickMedia {
themes_initialized = true;
}
+ static sf::Vector2f to_vec2f(const sf::Vector2i &vec) {
+ return sf::Vector2f(vec.x, vec.y);
+ }
+
BodyItem::BodyItem(std::string _title) :
visible(true),
dirty(false),
@@ -162,7 +176,7 @@ namespace QuickMedia {
num_visible_items(0),
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),
+ item_background(sf::Vector2f(1.0f, 1.0f), 10.0f, sf::Color(60, 65, 73), rounded_rectangle_shader),
reaction_background(sf::Vector2f(1.0f, 1.0f), 10.0f, sf::Color(33, 37, 44), rounded_rectangle_shader),
rounded_rectangle_mask_shader(rounded_rectangle_mask_shader)
{
@@ -188,10 +202,10 @@ namespace QuickMedia {
// 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_item_fits_in_body)
- return select_previous_item(false);
+ return select_previous_item(false, true);
for(int i = 0; i < num_visible_items - 1; ++i) {
- if(!select_previous_item(false))
+ if(!select_previous_item(false, true))
return false;
}
return true;
@@ -201,44 +215,62 @@ namespace QuickMedia {
// 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_item_fits_in_body)
- return select_next_item(false);
+ return select_next_item(false, true);
for(int i = 0; i < num_visible_items - 1; ++i) {
- if(!select_next_item(false))
+ if(!select_next_item(false, true))
return false;
}
return true;
}
- bool Body::select_previous_item(bool scroll_page_if_large_item) {
+ bool Body::select_previous_item(bool scroll_page_if_large_item, bool ignore_columns) {
if(items.empty())
return false;
- if(scroll_page_if_large_item && !selected_item_fits_in_body && !selected_line_top_visible) {
+ if(num_columns == 1 && scroll_page_if_large_item && !selected_item_fits_in_body && !selected_line_top_visible) {
selected_scrolled += 128.0f;
return true;
}
- const int new_selected_item = get_previous_visible_item(selected_item);
- if(new_selected_item == -1)
- return false;
+ int new_selected_item = selected_item;
+ for(int i = 0; i < (ignore_columns ? 1 : num_columns); ++i) {
+ const int prev = new_selected_item;
+ new_selected_item = get_previous_visible_item(new_selected_item);
+ if(new_selected_item == -1) {
+ if(i == 0)
+ return false;
+
+ new_selected_item = prev;
+ break;
+ }
+ }
selected_item = new_selected_item;
return true;
}
- bool Body::select_next_item(bool scroll_page_if_large_item) {
+ bool Body::select_next_item(bool scroll_page_if_large_item, bool ignore_columns) {
if(items.empty())
return false;
- if(scroll_page_if_large_item && !selected_item_fits_in_body && !selected_line_bottom_visible) {
+ if(num_columns == 1 && scroll_page_if_large_item && !selected_item_fits_in_body && !selected_line_bottom_visible) {
selected_scrolled -= 128.0f;
return true;
}
- const int new_selected_item = get_next_visible_item(selected_item);
- if(new_selected_item == -1)
- return false;
+ int new_selected_item = selected_item;
+ for(int i = 0; i < (ignore_columns ? 1 : num_columns); ++i) {
+ const int prev = new_selected_item;
+ new_selected_item = get_next_visible_item(new_selected_item);
+ if(new_selected_item == -1) {
+ if(i == 0)
+ return false;
+
+ new_selected_item = prev;
+ break;
+ }
+ }
selected_item = new_selected_item;
return true;
@@ -388,6 +420,8 @@ namespace QuickMedia {
bool Body::on_event(const sf::RenderWindow &window, const sf::Event &event, bool keyboard_navigation) {
if(keyboard_navigation && event.type == sf::Event::KeyPressed && !event.key.alt) {
+ const bool rendering_card_view = card_view && card_view_enabled;
+
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(true);
@@ -400,6 +434,24 @@ namespace QuickMedia {
if(!bottom_reached && on_bottom_reached)
on_bottom_reached();
return true;
+ } else if(rendering_card_view && selected_column > 0 && (event.key.code == sf::Keyboard::Left || (event.key.control && event.key.code == sf::Keyboard::H))) {
+ render_selected_item_bg = true;
+ const int new_selected_item = get_previous_visible_item(selected_item);
+ if(new_selected_item != -1) {
+ selected_item = new_selected_item;
+ } else if(on_top_reached) {
+ on_top_reached();
+ }
+ return true;
+ } else if(rendering_card_view && selected_column + 1 < num_columns && (event.key.code == sf::Keyboard::Right || (event.key.control && event.key.code == sf::Keyboard::L))) {
+ render_selected_item_bg = true;
+ const int new_selected_item = get_next_visible_item(selected_item);
+ if(new_selected_item != -1) {
+ selected_item = new_selected_item;
+ } else if(on_bottom_reached) {
+ on_bottom_reached();
+ }
+ return true;
} else if(event.key.code == sf::Keyboard::Home) {
render_selected_item_bg = true;
select_first_item(false);
@@ -468,12 +520,14 @@ namespace QuickMedia {
if(!show_drop_shadow)
return;
+ const sf::Color color(21, 25, 30);
const float height = 5.0f;
+
sf::Vertex gradient_points[4];
- gradient_points[0] = sf::Vertex(body_pos + sf::Vector2f(0.0f, 0.0f), sf::Color(21, 25, 30));
- gradient_points[1] = sf::Vertex(body_pos + sf::Vector2f(body_size.x, 0.0f), sf::Color(21, 25, 30));
- gradient_points[2] = sf::Vertex(body_pos + sf::Vector2f(body_size.x, height), sf::Color(0, 0, 0, 0));
- gradient_points[3] = sf::Vertex(body_pos + sf::Vector2f(0.0f, height), sf::Color(0, 0, 0, 0));
+ gradient_points[0] = sf::Vertex(body_pos + sf::Vector2f(0.0f, 0.0f), color);
+ gradient_points[1] = sf::Vertex(body_pos + sf::Vector2f(body_size.x, 0.0f), color);
+ gradient_points[2] = sf::Vertex(body_pos + sf::Vector2f(body_size.x, height), sf::Color(color.r, color.g, color.b, 0));
+ gradient_points[3] = sf::Vertex(body_pos + sf::Vector2f(0.0f, height), sf::Color(color.r, color.g, color.b, 0));
window.draw(gradient_points, 4, sf::Quads);
}
@@ -490,7 +544,9 @@ namespace QuickMedia {
items_dirty = DirtyState::FALSE;
}
- bool body_size_changed = std::abs(size.x - body_size.x) > 0.1f || std::abs(size.y - body_size.y) > 0.1f;
+ const bool rendering_card_view = card_view && card_view_enabled;
+
+ const 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)
body_size = size;
@@ -498,11 +554,14 @@ namespace QuickMedia {
const float scissor_y = pos.y;
pos.y = 0.0f;
- pos.x += body_spacing[body_theme].body_padding_horizontal;
+ if(!rendering_card_view)
+ pos.x += body_spacing[body_theme].body_padding_horizontal;
+
if(attach_side == AttachSide::TOP)
pos.y += body_spacing[body_theme].body_padding_vertical;
- size.x = std::max(0.0f, size.x - body_spacing[body_theme].body_padding_horizontal * 2.0f);
+ if(!rendering_card_view)
+ size.x = std::max(0.0f, size.x - body_spacing[body_theme].body_padding_horizontal * 2.0f);
float frame_time = frame_timer.restart().asSeconds();
if(frame_time > 0.01666f)
@@ -532,6 +591,8 @@ namespace QuickMedia {
selected_item_fits_in_body = true;
top_cut_off = false;
bottom_cut_off = false;
+ num_columns = 1;
+ selected_column = 0;
const int selected_item_diff = selected_item - prev_selected_item;
prev_selected_item = selected_item;
@@ -618,19 +679,23 @@ namespace QuickMedia {
if(swiping_enabled && grabbed_left_side)
pos.x += body_swipe_x;
- 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 < size.y;
- if(selected_item_fits_in_body)
- selected_scrolled = 0.0f;
+ if(!rendering_card_view) {
+ 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 < size.y;
+ if(selected_item_fits_in_body)
+ selected_scrolled = 0.0f;
+ }
const sf::Vector2u window_size = window.getSize();
const 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(!rendering_card_view) {
+ 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);
+ }
bool instant_move = body_size_changed;
if(target_y_set == TargetSetState::SET) {
@@ -640,155 +705,25 @@ namespace QuickMedia {
const float speed = 30.0f;
- 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));
- item_background_prev_height = item_background_new_height;
+ const sf::Vector2f item_background_size_diff = item_background_target_size - item_background_prev_size;
+ const float item_background_size_speed = instant_move ? 1000.0f : speed;
+ const sf::Vector2f item_background_new_size = item_background_prev_size + (item_background_size_diff * std::min(1.0f, frame_time * item_background_size_speed));
+ item_background_prev_size = item_background_new_size;
- const float item_background_pos_diff = item_background_target_pos_y - item_background_prev_pos_y;
+ const sf::Vector2f item_background_pos_diff = item_background_target_pos - item_background_prev_pos;
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));
+ sf::Vector2f item_background_new_pos = item_background_prev_pos + (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, size.y - item_background_new_height - body_spacing[body_theme].spacing_y);
- item_background_new_pos_y = std::max(item_background_new_pos_y, 0.0f);
+ item_background_new_pos.y = std::min(item_background_new_pos.y, size.y - item_background_new_size.y - body_spacing[body_theme].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));
- item_background_prev_pos_y = item_background_new_pos_y;
+ item_background_prev_pos = item_background_new_pos;
- if(prev_num_visible_items > 0 && render_selected_item_bg && body_theme == BODY_THEME_MINIMAL) {
- item_background.set_color(sf::Color(55, 60, 68));
- item_background.set_band(0.0f, 0.0f);
- 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);
-
- if(pos.y + selected_scrolled > 0.0f)
- selected_scrolled = 0.0f;
+ float body_total_height = 0.0f;
+ if(rendering_card_view) {
+ draw_card_view(window, pos, size, window_size, scissor_y);
} else {
- if(page_scroll < 0.0)
- page_scroll = 0.0;
- pos.y += size.y;
- pos.y += page_scroll;
- index = get_previous_visible_item(num_items);
-
- if(pos.y + selected_scrolled < size.y)
- selected_scrolled = 0.0f;
- }
-
- pos.y += selected_scrolled;
- const float pos_y_start = pos.y;
-
- BodyItem *prev_body_item = nullptr;
- const double height_move_speed = 1000.0f;
-
- // 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);
-
- 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();
- }
-
- 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 -= body_spacing[body_theme].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 top_y;
- if(attach_side == AttachSide::TOP)
- top_y = pos.y;
- else
- top_y = pos.y - (item->current_loaded_height + body_spacing[body_theme].spacing_y);
-
- if(top_y < 0.0f) {
- top_cut_off = true;
- if(index == selected_item)
- selected_line_top_visible = false;
- }
-
- if(top_y + item->current_loaded_height > size.y) {
- bottom_cut_off = true;
- if(index == selected_item)
- selected_line_bottom_visible = false;
- }
-
- const bool is_item_visible_in_body = top_y + item->current_loaded_height >= 0.0f && top_y <= 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 + body_spacing[body_theme].spacing_y);
- //page_scroll += add_height;
-
- //const float top_y_clamped = clamp(pos.y, 0.0f, size.y);
- //const float bottom_y_clamped = std::min(pos.y + item->current_loaded_height, size.y);
-
- //float offset_y = 0.0f;
- //if(pos.y < 0.0f)
- // offset_y = pos.y;
- //else if(pos.y > size.y)
- // offset_y = 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 + body_spacing[body_theme].spacing_y);
- } else {
- if(attach_side == AttachSide::TOP)
- pos.y += (item->current_loaded_height + body_spacing[body_theme].spacing_y);
- else
- pos.y -= (item->current_loaded_height + body_spacing[body_theme].spacing_y);
-
- 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;
- }
- }
-
- if(attach_side == AttachSide::BOTTOM && merge_with_previous)
- pos.y += body_spacing[body_theme].spacing_y;
-
- if(attach_side == AttachSide::TOP) {
- prev_body_item = item;
- index = get_next_visible_item(index);
- } else {
- index = prev_index;
- }
+ body_total_height = draw_list_view(window, pos, size, prev_num_visible_items, content_progress);
}
window.setView(prev_view);
@@ -797,14 +732,15 @@ namespace QuickMedia {
// TODO: Move up where scroll is limited? then we wont delay this by 1 frame creating a small scroll overflow only in the opposite direction of attach side.
// Also take |selected_scrolled| into consideration
// Limit scroll in the opposide direction of attach side, since the scroll is already limited for the attach side above (with a simple check)
- if(attach_side == AttachSide::TOP) {
- const float body_total_height = pos.y - pos_y_start;
- if(top_cut_off && !bottom_cut_off && body_total_height > (size.y - body_spacing[body_theme].body_padding_vertical))
- page_scroll = -(body_total_height - (size.y - body_spacing[body_theme].body_padding_vertical));
- } else {
- const float body_total_height = pos_y_start - pos.y;
- if(bottom_cut_off && !top_cut_off && body_total_height > size.y)
- page_scroll = (body_total_height - size.y);
+ if(!rendering_card_view) {
+ if(attach_side == AttachSide::TOP) {
+ if(top_cut_off && !bottom_cut_off && body_total_height > (size.y - body_spacing[body_theme].body_padding_vertical))
+ page_scroll = -(body_total_height - (size.y - body_spacing[body_theme].body_padding_vertical));
+ } else {
+ body_total_height = -body_total_height;
+ if(bottom_cut_off && !top_cut_off && body_total_height > size.y)
+ page_scroll = (body_total_height - size.y);
+ }
}
mouse_left_clicked = false;
@@ -818,8 +754,8 @@ namespace QuickMedia {
if(is_touch_enabled())
return;
- const float item_target_top_diff = item_background_target_pos_y - selected_scrolled - body_spacing[body_theme].body_padding_vertical;
- const float item_target_bottom_diff = (item_background_target_pos_y - selected_scrolled + item_background_target_height + body_spacing[body_theme].spacing_y) - size.y;
+ const float item_target_top_diff = item_background_target_pos.y - selected_scrolled - body_spacing[body_theme].body_padding_vertical;
+ const float item_target_bottom_diff = (item_background_target_pos.y - selected_scrolled + item_background_target_size.y + body_spacing[body_theme].spacing_y) - size.y;
if(item_target_top_diff < 0.0f || !selected_item_fits_in_body) {
//extra_scroll_target -= item_target_top_diff;
stuck_direction = StuckDirection::TOP;
@@ -855,10 +791,12 @@ namespace QuickMedia {
body_item->dirty = false;
// TODO: Find a way to optimize fromUtf8
sf::String str = sf::String::fromUtf8(body_item->get_title().begin(), body_item->get_title().end());
- if(body_item->title_text)
+ if(body_item->title_text) {
body_item->title_text->setString(std::move(str));
- else
+ body_item->title_text->setMaxWidth(width);
+ } else {
body_item->title_text = std::make_unique<Text>(std::move(str), false, std::floor(16 * get_ui_scale()), width, title_mark_urls);
+ }
body_item->title_text->setFillColor(body_item->get_title_color());
body_item->title_text->updateGeometry();
}
@@ -866,10 +804,12 @@ namespace QuickMedia {
if(body_item->dirty_description) {
body_item->dirty_description = false;
sf::String str = sf::String::fromUtf8(body_item->get_description().begin(), body_item->get_description().end());
- if(body_item->description_text)
+ if(body_item->description_text) {
body_item->description_text->setString(std::move(str));
- else
+ body_item->description_text->setMaxWidth(width);
+ } else {
body_item->description_text = std::make_unique<Text>(std::move(str), false, std::floor(14 * get_ui_scale()), width, true);
+ }
body_item->description_text->setFillColor(body_item->get_description_color());
body_item->description_text->updateGeometry();
}
@@ -877,10 +817,12 @@ namespace QuickMedia {
if(body_item->dirty_author) {
body_item->dirty_author = false;
sf::String str = sf::String::fromUtf8(body_item->get_author().begin(), body_item->get_author().end());
- if(body_item->author_text)
+ if(body_item->author_text) {
body_item->author_text->setString(std::move(str));
- else
+ body_item->author_text->setMaxWidth(width);
+ } else {
body_item->author_text = std::make_unique<Text>(std::move(str), true, std::floor(14 * get_ui_scale()), width);
+ }
body_item->author_text->setFillColor(body_item->get_author_color());
body_item->author_text->updateGeometry();
}
@@ -907,10 +849,11 @@ namespace QuickMedia {
*/
strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M", &message_tm);
- if(body_item->timestamp_text)
+ if(body_item->timestamp_text) {
body_item->timestamp_text->setString(time_str);
- else
+ } else {
body_item->timestamp_text = std::make_unique<sf::Text>(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(10 * get_ui_scale()));
+ }
body_item->timestamp_text->setFillColor(sf::Color(185, 190, 198, 100));
}
@@ -940,7 +883,7 @@ namespace QuickMedia {
sf::Vector2i Body::get_item_thumbnail_size(BodyItem *item) const {
sf::Vector2i content_size;
- sf::Vector2i thumbnail_max_size_scaled(std::floor(thumbnail_max_size.x * get_ui_scale()), std::floor(thumbnail_max_size.y * get_ui_scale()));
+ sf::Vector2i thumbnail_max_size_scaled = card_view_enabled ? card_max_image_size : sf::Vector2i(thumbnail_max_size.x * get_ui_scale(), thumbnail_max_size.y * get_ui_scale());
if(item->thumbnail_size.x > 0 && item->thumbnail_size.y > 0)
content_size = clamp_to_size(sf::Vector2i(std::floor(item->thumbnail_size.x * get_ui_scale()), std::floor(item->thumbnail_size.y * get_ui_scale())), thumbnail_max_size_scaled);
else
@@ -966,11 +909,8 @@ namespace QuickMedia {
return -1;
}
- static sf::Vector2f to_vec2f(const sf::Vector2i &vec) {
- return sf::Vector2f(vec.x, vec.y);
- }
-
void Body::draw_item(sf::RenderWindow &window, BodyItem *item, sf::Vector2f pos, sf::Vector2f size, bool include_embedded_item, bool is_embedded) {
+ // TODO: What about when |card_view| is used?
item->keep_alive_frames = 3;
get_item_height(item, size.x, true, false, false, -1);
draw_item(window, item, pos, size, size.y + body_spacing[body_theme].spacing_y, -1, Json::Value::nullSingleton(), include_embedded_item);
@@ -992,9 +932,9 @@ 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) {
+ void Body::handle_item_render(const sf::Vector2f pos, const float item_width, 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));
+ sf::FloatRect item_box(pos + body_pos, sf::Vector2f(item_width, 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);
@@ -1002,23 +942,341 @@ namespace QuickMedia {
}
if(item_index == selected_item) {
- item_background_target_pos_y = pos.y;
- item_background_target_height = item_height;
+ item_background_target_pos = pos;
+ item_background_target_size = sf::Vector2f(item_width, item_height);
if(target_y_set == TargetSetState::NOT_SET)
target_y_set = TargetSetState::SET;
}
}
- static sf::Color interpolate_colors(sf::Color source, sf::Color target, double progress) {
- int diff_r = (int)target.r - (int)source.r;
- int diff_g = (int)target.g - (int)source.g;
- int diff_b = (int)target.b - (int)source.b;
- int diff_a = (int)target.a - (int)source.a;
- return sf::Color(
- source.r + diff_r * progress,
- source.g + diff_g * progress,
- source.b + diff_b * progress,
- source.a + diff_a * progress);
+ float Body::draw_list_view(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const int prev_num_visible_items, const Json::Value &content_progress) {
+ const int num_items = items.size();
+
+ if(prev_num_visible_items > 0 && render_selected_item_bg && body_theme == BODY_THEME_MINIMAL) {
+ item_background.set_position(sf::Vector2f(pos.x, item_background_prev_pos.y));
+ item_background.set_size(item_background_prev_size);
+ item_background.set_color(sf::Color(60, 65, 73));
+ item_background.set_band(sf::Vector2f(0.0f, 0.0f), sf::Vector2f(0.0f, 0.0f));
+ 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);
+
+ if(pos.y + selected_scrolled > 0.0f)
+ selected_scrolled = 0.0f;
+ } else {
+ if(page_scroll < 0.0)
+ page_scroll = 0.0;
+ pos.y += size.y;
+ pos.y += page_scroll;
+ index = get_previous_visible_item(num_items);
+
+ if(pos.y + selected_scrolled < size.y)
+ selected_scrolled = 0.0f;
+ }
+
+ pos.y += selected_scrolled;
+ const float pos_y_start = pos.y;
+
+ BodyItem *prev_body_item = nullptr;
+
+ // 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);
+
+ 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();
+ }
+
+ 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 -= body_spacing[body_theme].spacing_y;
+
+ get_item_height(item, size.x, false, true, merge_with_previous, index);
+ item->current_loaded_height = item->last_loaded_height;
+
+ float top_y;
+ if(attach_side == AttachSide::TOP)
+ top_y = pos.y;
+ else
+ top_y = pos.y - (item->current_loaded_height + body_spacing[body_theme].spacing_y);
+
+ if(top_y < 0.0f) {
+ top_cut_off = true;
+ if(index == selected_item)
+ selected_line_top_visible = false;
+ }
+
+ if(top_y + item->current_loaded_height > size.y) {
+ bottom_cut_off = true;
+ if(index == selected_item)
+ selected_line_bottom_visible = false;
+ }
+
+ const bool is_item_visible_in_body = top_y + item->current_loaded_height >= 0.0f && top_y <= size.y;
+ if(is_item_visible_in_body || index == selected_item) {
+ get_item_height(item, size.x, true, true, merge_with_previous, index);
+ item->current_loaded_height = item->last_loaded_height;
+ if(attach_side == AttachSide::BOTTOM)
+ pos.y -= (item->current_loaded_height + body_spacing[body_theme].spacing_y);
+ //page_scroll += add_height;
+
+ //const float top_y_clamped = clamp(pos.y, 0.0f, size.y);
+ //const float bottom_y_clamped = std::min(pos.y + item->current_loaded_height, size.y);
+
+ //float offset_y = 0.0f;
+ //if(pos.y < 0.0f)
+ // offset_y = pos.y;
+ //else if(pos.y > size.y)
+ // offset_y = 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(pos, size.x, 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 + body_spacing[body_theme].spacing_y);
+ } else {
+ if(attach_side == AttachSide::TOP)
+ pos.y += (item->current_loaded_height + body_spacing[body_theme].spacing_y);
+ else
+ pos.y -= (item->current_loaded_height + body_spacing[body_theme].spacing_y);
+
+ 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;
+ }
+ }
+
+ if(attach_side == AttachSide::BOTTOM && merge_with_previous)
+ pos.y += body_spacing[body_theme].spacing_y;
+
+ if(attach_side == AttachSide::TOP) {
+ prev_body_item = item;
+ index = get_next_visible_item(index);
+ } else {
+ index = prev_index;
+ }
+ }
+
+ return pos.y - pos_y_start;
+ }
+
+ void Body::draw_card_view(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, sf::Vector2u window_size, float scissor_y) {
+ num_columns = size.x / card_width;
+
+ int space_left_column = size.x - (num_columns * card_width);
+ int space_left_column_each = 0;
+ // TODO: Spacing for 1 column
+ if(num_columns > 1) {
+ space_left_column_each = space_left_column / (2 + num_columns - 1);
+ if(space_left_column_each < min_column_spacing) {
+ num_columns = (size.x + min_column_spacing) / (card_width + min_column_spacing);
+ //space_left_column_each = min_column_spacing;
+ space_left_column = size.x - (num_columns * card_width);
+ space_left_column_each = num_columns <= 1 ? 0 : space_left_column / (2 + num_columns - 1);
+ }
+ }
+
+ if(num_columns <= num_columns_switch_to_list) {
+ num_columns = 1;
+ card_view_enabled = false;
+ draw(window, body_pos, body_size);
+ card_view_enabled = true;
+ return;
+ }
+
+ const int space_left_row_each = space_left_column_each;
+
+ //item_background.set_band_color(sf::Color(60, 65, 73));
+ //item_background.set_band(item_background_prev_pos_y - pos.y, item_background_prev_height);
+
+ if(page_scroll > 0.0)
+ page_scroll = 0.0;
+
+ int item_index = 0;
+ int drawn_column_index = 0;
+ int num_visible_rows = 1;
+ const int num_items = items.size();
+ sf::Vector2f pos_offset(space_left_column_each, page_scroll);
+
+ while(item_index < num_items) {
+ BodyItem *item = items[item_index].get();
+
+ if(pos_offset.y + card_height <= -body_spacing[body_theme].body_padding_vertical) {
+ top_cut_off = true;
+ if(item_index == selected_item)
+ selected_line_top_visible = false;
+ }
+
+ if(pos_offset.y >= size.y) {
+ bottom_cut_off = true;
+ if(item_index == selected_item)
+ selected_line_bottom_visible = false;
+ }
+
+ if(item_index == selected_item)
+ selected_column = drawn_column_index;
+
+ handle_item_render(pos + pos_offset, card_width, card_height, item_index);
+
+ if(item->visible && pos_offset.y + card_height > -body_spacing[body_theme].body_padding_vertical && pos_offset.y < size.y) {
+ 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);
+
+ item_background.set_position(pos + pos_offset);
+ item_background.set_size(sf::Vector2f(card_width, card_height));
+ item_background.set_color(sf::Color(33, 37, 44));
+ item_background.set_band(item_background_prev_pos - (pos + pos_offset), item_background_prev_size);
+ item_background.set_band_color(sf::Color(60, 65, 73));
+ item_background.draw(window);
+
+ {
+ get_item_height(item, card_max_image_size.x);
+ sf::Vector2i thumbnail_size = get_item_thumbnail_size(item);
+ std::shared_ptr<ThumbnailData> item_thumbnail;
+ if(draw_thumbnails && !item->thumbnail_url.empty())
+ item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, thumbnail_size);
+
+ float image_height = 0.0f;
+ if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) {
+ image.setTexture(item_thumbnail->texture, true);
+ auto image_size = image.getTexture()->getSize();
+ sf::Vector2f image_size_f(image_size.x, image_size.y);
+ sf::Vector2f content_size = to_vec2f(thumbnail_size);
+ auto new_image_size = clamp_to_size(image_size_f, content_size);
+ auto image_scale = get_ratio(image_size_f, new_image_size);
+ image.setScale(image_scale);
+ image.setPosition(pos + pos_offset + sf::Vector2f(card_padding_x, card_padding_y) + sf::Vector2f(card_max_image_size.x * 0.5f, 0.0f) - sf::Vector2f(new_image_size.x * 0.5f, 0.0f));
+ image_height = new_image_size.y;
+ if(thumbnail_mask_shader && item->thumbnail_mask_type == ThumbnailMaskType::CIRCLE) {
+ thumbnail_mask_shader->setUniform("resolution", new_image_size);
+ window.draw(image, thumbnail_mask_shader);
+ } else if(rounded_rectangle_mask_shader) {
+ rounded_rectangle_mask_shader->setUniform("radius", 10.0f);
+ rounded_rectangle_mask_shader->setUniform("resolution", new_image_size);
+ window.draw(image, rounded_rectangle_mask_shader);
+ } else {
+ window.draw(image);
+ }
+ } else if(!item->thumbnail_url.empty()) {
+ sf::Vector2f content_size = to_vec2f(thumbnail_size);
+ sf::Vector2f loading_icon_size(loading_icon.getTexture()->getSize().x, loading_icon.getTexture()->getSize().y);
+ auto new_loading_icon_size = clamp_to_size(loading_icon_size, content_size);
+ loading_icon.setPosition(pos + pos_offset + sf::Vector2f(card_padding_x, card_padding_y) + to_vec2f(card_max_image_size) * 0.5f);
+ loading_icon.setScale(get_ratio(loading_icon_size, new_loading_icon_size));
+ loading_icon.setRotation(elapsed_time_sec * 400.0);
+ window.draw(loading_icon);
+ image_height = content_size.y;
+ }
+
+ const float text_padding = item_thumbnail ? card_image_text_padding : 0.0f;
+
+ sf::Vector2f text_pos = sf::Vector2f(pos.x, scissor_y + body_spacing[body_theme].body_padding_vertical) + pos_offset + sf::Vector2f(card_padding_x, card_padding_y) + sf::Vector2f(0.0f, image_height + text_padding);
+ const float text_height = (card_height - card_padding_y * 2.0f) - image_height - text_padding;
+
+ const float underflow_text = text_pos.y - scissor_y;
+ const float underflow_height = underflow_text < 0.0f ? text_height + underflow_text : text_height;
+ sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, underflow_height));
+ new_view.setViewport(sf::FloatRect(0.0f, std::max(text_pos.y, scissor_y) / (float)window_size.y, 1.0f, underflow_height / (float)window_size.y));
+ window.setView(new_view);
+
+ text_pos.y = std::min(0.0f, underflow_text);
+ float text_offset_y = 0.0f;
+ if(item->author_text) {
+ item->author_text->setPosition(text_pos);
+ item->author_text->setMaxWidth(card_max_image_size.x);
+ item->author_text->draw(window);
+ text_offset_y += item->author_text->getHeight();
+ }
+
+ if(item->title_text) {
+ item->title_text->setPosition(text_pos + sf::Vector2f(0.0f, text_offset_y));
+ item->title_text->setMaxWidth(card_max_image_size.x);
+ item->title_text->draw(window);
+ text_offset_y += item->title_text->getHeight();
+ }
+
+ if(item->description_text) {
+ item->description_text->setPosition(text_pos + sf::Vector2f(0.0f, text_offset_y));
+ item->description_text->setMaxWidth(card_max_image_size.x);
+ item->description_text->draw(window);
+ text_offset_y += item->description_text->getHeight();
+ }
+
+ const float gradient_height = 5.0f;
+ if(text_offset_y >= text_height - gradient_height) {
+ const sf::Vector2f card_bottom(text_pos.x, text_height);
+ const sf::Color color = item_index == selected_item ? sf::Color(60, 65, 73) : sf::Color(33, 37, 44);
+
+ sf::Vertex gradient_points[4];
+ gradient_points[0] = sf::Vertex(card_bottom + sf::Vector2f(0.0f, -gradient_height), sf::Color(color.r, color.g, color.b, 0));
+ gradient_points[1] = sf::Vertex(card_bottom + sf::Vector2f(card_max_image_size.x, -gradient_height), sf::Color(color.r, color.g, color.b, 0));
+ gradient_points[2] = sf::Vertex(card_bottom + sf::Vector2f(card_max_image_size.x, 0.0f), color);
+ gradient_points[3] = sf::Vertex(card_bottom + sf::Vector2f(0.0f, 0.0f), color);
+ window.draw(gradient_points, 4, sf::Quads);
+ }
+ }
+
+ ++num_visible_items;
+ if(first_visible_item == -1 || item_index < first_visible_item)
+ first_visible_item = item_index;
+ if(last_visible_item == -1 || item_index > last_visible_item)
+ last_visible_item = item_index;
+ } else {
+ 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;
+ }
+ }
+
+ if(item->visible) {
+ pos_offset.x += card_width + space_left_column_each;
+ ++drawn_column_index;
+
+ if(drawn_column_index == num_columns) {
+ drawn_column_index = 0;
+ ++num_visible_rows;
+ pos_offset.x = space_left_column_each;
+ pos_offset.y += card_height + space_left_row_each;
+ }
+ }
+
+ ++item_index;
+ }
+
+ /*const float body_total_height = num_visible_rows * (card_height + space_left_row_each);
+ if(top_cut_off && !bottom_cut_off && body_total_height > size.y)
+ page_scroll = -(body_total_height - (size.y - body_spacing[body_theme].body_padding_vertical));*/
}
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) {
@@ -1040,8 +1298,8 @@ namespace QuickMedia {
item_background.set_size(sf::Vector2f(size.x, item_height));
item_background.set_position(item_pos);
item_background.set_color(sf::Color(33, 37, 44));
- item_background.set_band(item_background_prev_pos_y - pos.y, item_background_prev_height);
- item_background.set_band_color(sf::Color(55, 60, 68));
+ item_background.set_band(item_background_prev_pos - pos, item_background_prev_size);
+ item_background.set_band_color(sf::Color(60, 65, 73));
item_background.draw(window);
}
@@ -1103,12 +1361,13 @@ namespace QuickMedia {
text_offset_x += body_spacing[body_theme].image_padding_x + thumbnail_size.x;
}
+ const float text_max_width = size.x - text_offset_x - body_spacing[body_theme].image_padding_x;
const float text_offset_y = std::floor(6.0f * get_ui_scale());
const float timestamp_text_y = std::floor(item_pos.y + padding_y - text_offset_y);
if(item->author_text && !merge_with_previous) {
item->author_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - text_offset_y));
- item->author_text->setMaxWidth(size.x - text_offset_x - body_spacing[body_theme].image_padding_x);
+ item->author_text->setMaxWidth(text_max_width);
item->author_text->draw(window);
sf::Vector2f replies_text_pos = item->author_text->getPosition() + sf::Vector2f(0.0f, 5.0f);
@@ -1153,7 +1412,7 @@ namespace QuickMedia {
//window.draw(title_text);
if(item->title_text) {
item->title_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - text_offset_y));
- item->title_text->setMaxWidth(size.x - text_offset_x - body_spacing[body_theme].image_padding_x);
+ item->title_text->setMaxWidth(text_max_width);
item->title_text->draw(window);
item_pos.y += item->title_text->getHeight() - 2.0f + std::floor(3.0f * get_ui_scale());
}
@@ -1161,7 +1420,7 @@ namespace QuickMedia {
if(item->description_text) {
float height_offset = 0.0f;
item->description_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - text_offset_y) + height_offset);
- item->description_text->setMaxWidth(size.x - text_offset_x - body_spacing[body_theme].image_padding_x);
+ item->description_text->setMaxWidth(text_max_width);
item->description_text->draw(window);
item_pos.y += item->description_text->getHeight() - 2.0f;
}
@@ -1173,7 +1432,7 @@ namespace QuickMedia {
// TODO: Fix first row wrap-around
for(int i = 0; i < item->reactions.size(); ++i) {
auto &reaction = item->reactions[i];
- reaction.text->setMaxWidth(size.x - text_offset_x - body_spacing[body_theme].image_padding_x);
+ reaction.text->setMaxWidth(text_max_width);
reaction.text->updateGeometry();
reaction_max_height = std::max(reaction_max_height, reaction.text->getHeight());
reaction.text->setPosition(std::floor(item_pos.x + text_offset_x + reaction_offset_x + body_spacing[body_theme].reaction_background_padding_x), std::floor(item_pos.y + padding_y - 4.0f + body_spacing[body_theme].reaction_background_padding_y));
@@ -1251,8 +1510,10 @@ namespace QuickMedia {
// image_height = content_size.y;
}
+ const float text_max_width = (card_view && card_view_enabled) ? width : (width - text_offset_x - body_spacing[body_theme].image_padding_x);
+
if(load_texture)
- update_dirty_state(item, width - text_offset_x - body_spacing[body_theme].image_padding_x);
+ update_dirty_state(item, text_max_width);
float item_height = 0.0f;
if(item->title_text) {
@@ -1277,7 +1538,7 @@ namespace QuickMedia {
float reaction_max_height = 0.0f;
for(int i = 0; i < item->reactions.size(); ++i) {
auto &reaction = item->reactions[i];
- reaction.text->setMaxWidth(width - text_offset_x - body_spacing[body_theme].image_padding_x);
+ reaction.text->setMaxWidth(text_max_width);
reaction.text->updateGeometry();
reaction_max_height = std::max(reaction_max_height, reaction.text->getHeight());
reaction_offset_x += reaction.text->getWidth() + body_spacing[body_theme].reaction_background_padding_x * 2.0f + body_spacing[body_theme].reaction_spacing_x;