aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-05-17 04:09:47 +0200
committerdec05eba <dec05eba@protonmail.com>2021-05-17 04:09:47 +0200
commit28168bb3a63b7506a79441d6ce5ed312ba6f6e1a (patch)
treec68ca0a70f3cefd4d490abcc754995641d04ddf6
parente37a174979059a97c342c51b741f4f89609830c4 (diff)
Make body movement smooth, async load video related videos to allow cancellation with esc
-rw-r--r--README.md2
-rw-r--r--TODO1
-rw-r--r--include/Body.hpp8
-rw-r--r--src/Body.cpp113
-rw-r--r--src/QuickMedia.cpp21
-rw-r--r--src/main.cpp1
-rw-r--r--src/plugins/Matrix.cpp4
7 files changed, 117 insertions, 33 deletions
diff --git a/README.md b/README.md
index c973bad..e469106 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ EXAMPLES:
tabbed -c -k quickmedia launcher -e
```
## Installation
-If you are running arch linux then you can install QuickMedia from aur (https://aur.archlinux.org/packages/quickmedia-git/), otherwise you will need to use [sibs](https://git.dec05eba.com/sibs/) to build QuickMedia manually.
+If you are running arch linux then you can install QuickMedia from aur (https://aur.archlinux.org/packages/quickmedia-git/), otherwise you will need to use [sibs](https://git.dec05eba.com/sibs/) to build QuickMedia manually using this command: `sibs build --release`. Then the quickmedia executable is available at `sibs-build/<platform-name>/release/quickmedia`.
## Controls
Press `Arrow up` / `Arrow down` or `Ctrl+K` / `Ctrl+J` to navigate the menu and also to scroll to the previous/next image when viewing manga in scroll mode. Alternatively you can use the mouse scroll to scroll to the previous/next manga in scroll mode.\
Press `Arrow left` / `Arrow right` or `Ctrl+H` / `Ctrl+L` to switch tab.\
diff --git a/TODO b/TODO
index af24b76..5f112e2 100644
--- a/TODO
+++ b/TODO
@@ -118,7 +118,6 @@ Check if get_page handlers in pages need to check if next batch is valid. If the
Cloudflare kicks in when downloading manga on manganelo.. figure out a way to bypass it. This doesn't seem to happen when using python requests as is done in AutoMedia.
Replace cppcodec with another library for base64 url encoding/decoding. Its way too large for what it does.
Revert back to old fuzzy search code or use levenshtein distance, then reorder items by best match. This could be done by having a second vector of indices and use that vector everywhere body items by index is accessed (including selected_item). Also perform the search in Body::draw when search term has been modified. This allows us to automatically update that new vector.
-Using a VertexBuffer in Text makes the text quickly glitch out. Why does this happen?
Update subscriptions page either with f5 and automatically update it when adding/removing subscriptions.
Update room name and topic text in ui when they change.
Support webp directly without using ffmpeg to convert it to a png.
diff --git a/include/Body.hpp b/include/Body.hpp
index 3106cc3..8a3bce5 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -280,9 +280,12 @@ namespace QuickMedia {
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;
// 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::RectangleShape item_separator;
sf::Sprite image;
sf::Sprite loading_icon;
int num_visible_items;
@@ -290,6 +293,7 @@ namespace QuickMedia {
bool last_item_fully_visible;
int first_fully_visible_item;
int last_fully_visible_item;
+ int first_visible_item = -1;
int last_visible_item = -1;
sf::Clock draw_timer;
sf::Clock frame_timer;
@@ -318,5 +322,7 @@ namespace QuickMedia {
RoundedRectangle item_background;
RoundedRectangle reaction_background;
bool render_selected_item_bg = true;
+ float item_background_target_pos_y = 0.0f;
+ float item_background_target_height = 0.0f;
};
} \ No newline at end of file
diff --git a/src/Body.cpp b/src/Body.cpp
index ffafb90..d7a4456 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -284,6 +284,8 @@ namespace QuickMedia {
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() {
@@ -294,6 +296,8 @@ namespace QuickMedia {
//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() {
@@ -304,6 +308,8 @@ namespace QuickMedia {
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(BodyItems new_items) {
@@ -481,6 +487,9 @@ namespace QuickMedia {
sf::Vector2f scissor_pos = pos;
sf::Vector2f scissor_size = size;
const float start_y = pos.y;
+ float frame_time = frame_timer.restart().asSeconds();
+ if(frame_time > 2.0f)
+ frame_time = 2.0f;
body_pos = pos;
bool body_size_changed = std::abs(size.x - body_size.x) > 0.1f || std::abs(size.y - body_size.y) > 0.1f;
@@ -489,26 +498,33 @@ namespace QuickMedia {
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((attach_side == AttachSide::BOTTOM && scroll_past_first) || (attach_side == AttachSide::TOP && 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;
- if(has_scrolled_with_input) {
- clamp_selected_item_to_body_count--;
- if(clamp_selected_item_to_body_count < 0)
- clamp_selected_item_to_body_count = 0;
- }
+ 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()) {
- float frame_time = frame_timer.restart().asSeconds();
- if(frame_time > 2.0f)
- frame_time = 2.0f;
-
const sf::Vector2f mouse_pos_diff(mouse_pos_raw.x - mouse_pos.x, mouse_pos_raw.y - mouse_pos.y);
const float move_speed = 25.0f;
- mouse_pos.x += (mouse_pos_diff.x * frame_time * move_speed);
- mouse_pos.y += (mouse_pos_diff.y * frame_time * move_speed);
+ 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));
sf::Vector2f mouse_smooth_diff(mouse_pos.x - prev_mouse_pos.x, mouse_pos.y - prev_mouse_pos.y);
prev_mouse_pos = mouse_pos;
+ if(mouse_left_pressed && (scroll_past_first || scroll_past_last)) {
+ mouse_scroll_accel.x = 0.0f;
+ mouse_scroll_accel.y = 0.0f;
+ mouse_pos.x = mouse_pos_raw.x;
+ mouse_pos.y = mouse_pos_raw.y;
+ }
+
if(items_cut_off) {
if(mouse_left_pressed) {
selected_scrolled += mouse_smooth_diff.y;
@@ -531,14 +547,14 @@ namespace QuickMedia {
clamp_selection();
// TODO: Cache this
if(on_top_reached) {
- int first_visible_item = -1;
+ int first_visible_item_n = -1;
for(int i = 0; i <= selected_item; ++i) {
if(items[i]->visible) {
- first_visible_item = i;
+ first_visible_item_n = i;
break;
}
}
- if(first_visible_item == first_fully_visible_item)
+ if(first_visible_item_n == first_fully_visible_item)
on_top_reached();
}
} else if(mouse_scroll_accel.y < -0.1 && last_fully_visible_item != -1) {
@@ -546,14 +562,14 @@ namespace QuickMedia {
clamp_selection();
// TODO: Cache this
if(on_bottom_reached) {
- int last_visible_item = -1;
+ int last_visible_item_n = -1;
for(int i = items.size() - 1; i >= selected_item; --i) {
if(items[i]->visible) {
- last_visible_item = i;
+ last_visible_item_n = i;
break;
}
}
- if(last_visible_item == last_fully_visible_item)
+ if(last_visible_item_n == last_fully_visible_item)
on_bottom_reached();
}
}
@@ -574,10 +590,28 @@ namespace QuickMedia {
}
}
- item_separator.setFillColor(line_separator_color);
+ const double scroll_diff = extra_scroll_target - extra_scroll_current;
+ const double scroll_move_speed = 25.0;
+ 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;
+
+ selected_scrolled += scroll_smooth_diff;
+
+ bool selected_item_fits_on_screen = selected_item_height <= size.y;
+ if(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 || selected_item == first_fully_visible_item || selected_item == last_fully_visible_item)) {
+ page_scroll += scroll_smooth_diff;
+ 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;
@@ -594,7 +628,8 @@ namespace QuickMedia {
offset_to_top = 0.0f;
offset_to_bottom = 0.0f;
mouse_left_clicked = false;
- last_visible_item = -1;
+ //item_background_target_pos_y = body_pos.y;
+ //item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
return;
}
@@ -615,6 +650,7 @@ namespace QuickMedia {
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());
@@ -628,10 +664,13 @@ namespace QuickMedia {
++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);
@@ -646,6 +685,8 @@ namespace QuickMedia {
--i;
}
prev_selected_item = selected_item;
+ if(selected_item_fits_on_screen)
+ extra_scroll_target -= (page_scroll - scroll_before);
}
bool merge_with_previous = false;
@@ -669,7 +710,8 @@ namespace QuickMedia {
} else if(attach_side == AttachSide::BOTTOM) {
if(last_item_fully_visible) {
page_scroll += offset_to_bottom;
- } else {
+ } 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);
}
}
@@ -678,7 +720,7 @@ namespace QuickMedia {
selected_item_height = get_item_height(items[selected_item].get(), size.x, true, true, merge_with_previous, selected_item);
selected_item_height += spacing_y;
- bool selected_item_fits_on_screen = selected_item_height <= size.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;
@@ -719,6 +761,25 @@ namespace QuickMedia {
sf::Vector2u window_size = window.getSize();
+ if(prev_num_visible_items > 0) {
+ 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 = 50.0f;
+ 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 = 50.0f;
+ 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) {
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(scissor_pos.x, (int)window_size.y - (int)scissor_pos.y - (int)scissor_size.y, scissor_size.x, scissor_size.y);
+ item_background.draw(window);
+ glDisable(GL_SCISSOR_TEST);
+ }
+ }
+
sf::Vector2f prev_pos = pos;
int i;
for(i = selected_item - 1; i >= 0;) {
@@ -753,6 +814,7 @@ namespace QuickMedia {
draw_item(window, item.get(), prev_pos, size, item_height, i, content_progress, true, merge_with_previous);
glDisable(GL_SCISSOR_TEST);
++num_visible_items;
+ first_visible_item = i;
last_visible_item = i;
if(first_item_fully_visible)
@@ -810,6 +872,8 @@ namespace QuickMedia {
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;
int next_body_item_index = get_next_visible_item(i);
@@ -1125,10 +1189,9 @@ namespace QuickMedia {
//item_separator.setPosition(item_pos + sf::Vector2f(10.0f, std::floor(item_height + spacing_y * 0.5f)));
//window.draw(item_separator);
- if(render_selected_item_bg && item_index == selected_item) {
- item_background.set_position(item_pos);
- item_background.set_size(sf::Vector2f(size.x, item_height));
- item_background.draw(window);
+ if(item_index == selected_item) {
+ item_background_target_pos_y = item_pos.y;
+ item_background_target_height = item_height;
}
float text_offset_x = padding_x;
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index cd4a279..cd1377a 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -561,7 +561,6 @@ namespace QuickMedia {
}
void Program::init(Window parent_window, std::string &program_path) {
- XInitThreads();
disp = XOpenDisplay(NULL);
if (!disp)
throw std::runtime_error("Failed to open display to X11 server");
@@ -2187,8 +2186,15 @@ namespace QuickMedia {
return;
channel_url.clear();
- // TODO: Remove this and use lazy_fetch instead
- related_videos = video_page->get_related_media(video_url, channel_url);
+ TaskResult load_related_media_result = run_task_with_loading_screen([video_page, &related_videos, &video_url, &channel_url]{
+ related_videos = video_page->get_related_media(video_url, channel_url);
+ return true;
+ });
+
+ if(load_related_media_result == TaskResult::CANCEL) {
+ current_page = previous_page;
+ return;
+ }
// TODO: Make this also work for other video plugins
if(strcmp(plugin_name, "youtube") != 0 || resume_video)
@@ -2297,7 +2303,7 @@ namespace QuickMedia {
KeySym pressed_keysym = XKeycodeToKeysym(disp, xev.xkey.keycode, 0);
#pragma GCC diagnostic pop
bool pressing_ctrl = (CLEANMASK(xev.xkey.state) == ControlMask);
- if(pressed_keysym == XK_Escape || pressed_keysym == XK_BackSpace || pressed_keysym == XK_q) {
+ if(pressed_keysym == XK_Escape || pressed_keysym == XK_q || pressed_keysym == XK_BackSpace) {
current_page = previous_page;
break;
} else if(pressed_keysym == XK_f && pressing_ctrl) {
@@ -2402,7 +2408,7 @@ namespace QuickMedia {
if(new_video_url.empty() && parent_page && parent_body_page) {
BodyItems new_body_items;
const int fetch_page = (*parent_body_page) + 1;
- run_task_with_loading_screen([parent_page, parent_page_search, fetch_page, &new_body_items] {
+ TaskResult load_next_page_result = run_task_with_loading_screen([parent_page, parent_page_search, fetch_page, &new_body_items] {
if(parent_page->get_page(parent_page_search, fetch_page, new_body_items) != PluginResult::OK) {
fprintf(stderr, "Failed to get next page (page %d)\n", fetch_page);
return false;
@@ -2418,6 +2424,11 @@ namespace QuickMedia {
related_videos = std::move(new_body_items);
find_next_video();
}
+
+ if(load_next_page_result == TaskResult::CANCEL) {
+ current_page = previous_page;
+ continue;
+ }
}
// If there are no videos to play, then dont play any...
diff --git a/src/main.cpp b/src/main.cpp
index 89d824d..15041d5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,6 +2,7 @@
#include <unistd.h>
int main(int argc, char **argv) {
+ XInitThreads();
setlocale(LC_ALL, "C"); // Sigh... stupid C
QuickMedia::Program program;
return program.run(argc, argv);
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 8679a54..1d471fc 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -920,12 +920,16 @@ namespace QuickMedia {
PluginResult MatrixInviteDetailsPage::submit(const std::string &title, const std::string&, std::vector<Tab>&) {
if(title == "Accept") {
if(matrix->join_room(room_id) == PluginResult::OK) {
+ // TODO: Wait for room invite list change from the server instead of removing room here.
+ // Then the invite list can be updated when accepting/declining an invite in another client.
invites_page->remove_body_item_by_room_id(room_id);
} else {
show_notification("QuickMedia", "Failed to accept the room invite", Urgency::CRITICAL);
}
} else if(title == "Decline") {
if(matrix->leave_room(room_id) == PluginResult::OK) {
+ // TODO: Wait for room invite list change from the server instead of removing room here.
+ // Then the invite list can be updated when accepting/declining an invite in another client.
invites_page->remove_body_item_by_room_id(room_id);
} else {
show_notification("QuickMedia", "Failed to decline the room invite", Urgency::CRITICAL);