From e308d77b06405b91885cf6f97c0dc2a1b70679ef Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 11 May 2021 14:24:52 +0200 Subject: Improve file saving gui --- src/QuickMedia.cpp | 298 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 245 insertions(+), 53 deletions(-) (limited to 'src/QuickMedia.cpp') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index b4fa915..13b9369 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -29,6 +29,7 @@ #include "../include/ResourceLoader.hpp" #include "../include/Utils.hpp" #include "../include/Tabs.hpp" +#include "../include/gui/Button.hpp" #include "../external/hash-library/sha256.h" #include @@ -599,7 +600,7 @@ namespace QuickMedia { window.create(x11_window); - if(program_path.back() != '/') + if(!program_path.empty() && program_path.back() != '/') program_path += '/'; resources_root = "/usr/share/quickmedia/"; @@ -1531,6 +1532,7 @@ namespace QuickMedia { if(tabs[selected_tab].page->is_single_page()) { if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->clear(); if(new_tabs.size() == 1 && !new_tabs[0].page) { + tabs[selected_tab].body->clear_thumbnails(); tabs[selected_tab].body = std::move(new_tabs[0].body); tabs[selected_tab].page->submit_body_item = prev_selected_item; return; @@ -1854,6 +1856,7 @@ namespace QuickMedia { if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && associated_data.fetch_future.ready()) { if(!associated_data.search_text_updated) { FetchResult fetch_result = associated_data.fetch_future.get(); + tabs[i].body->clear_thumbnails(); tabs[i].body->items = std::move(fetch_result.body_items); tabs[i].body->select_first_item(); associated_data.fetched_page = 0; @@ -2270,7 +2273,7 @@ namespace QuickMedia { } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::C && event.key.control) { save_video_url_to_clipboard(); } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::S && event.key.control) { - download_async_gui(original_video_url, true, force_no_video); + download_async_gui(original_video_url, true, no_video); } } handle_window_close(); @@ -2287,7 +2290,7 @@ namespace QuickMedia { } else if(pressed_keysym == XK_f && pressing_ctrl) { window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE); } else if(pressed_keysym == XK_s && pressing_ctrl) { - download_async_gui(original_video_url, true, force_no_video); + download_async_gui(original_video_url, true, no_video); } else if(pressed_keysym == XK_r && pressing_ctrl) { if(!cursor_visible) window.setMouseCursorVisible(true); @@ -2595,7 +2598,7 @@ namespace QuickMedia { }; } else if(website_url && website_url[0] != '\0') { std::string website_url_str = website_url; - if(website_url_str.back() != '/') + if(!website_url_str.empty() && website_url_str.back() != '/') website_url_str.push_back('/'); extra_args = { CommandArg { "-H", "referer: " + std::move(website_url_str) }, @@ -4733,7 +4736,7 @@ namespace QuickMedia { if(selected_item_message) { MessageType message_type = selected_item_message->type; if(!selected->url.empty() && (message_type == MessageType::VIDEO || message_type == MessageType::IMAGE || message_type == MessageType::AUDIO)) { - download_async_gui(selected->url, false, force_no_video); + download_async_gui(selected->url, false, no_video); return true; } } @@ -5919,60 +5922,57 @@ namespace QuickMedia { bool no_video; }; - static const char* get_filename(const char *path) { - const char *p = (const char*)memrchr(path, '/', strlen(path)); - return p ? p + 1 : path; + static int accumulate_string(char *data, int size, void *userdata) { + std::string *str = (std::string*)userdata; + if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable + return 1; + str->append(data, size); + return 0; } - class ConfirmationPage : public Page { - public: - ConfirmationPage(Program *program, FileManagerPage *file_manager_page, bool *file_overwrite, const std::string &title) : Page(program), file_manager_page(file_manager_page), file_overwrite(file_overwrite), title(title) {} - const char* get_title() const override { return title.c_str(); } - PluginResult submit(const std::string &title, const std::string&, std::vector&) override { - if(title == "Yes") { - *file_overwrite = true; - file_manager_page->close = true; - } else { - *file_overwrite = false; + void Program::download_page(const char *url, bool download_use_youtube_dl) { + window.setTitle("QuickMedia - Select where you want to save " + std::string(url)); + + std::string filename; + TaskResult task_result = run_task_with_loading_screen([this, url, &filename]{ + std::string json_str; + std::vector args = { "youtube-dl", "--skip-download", "--print-json", "--no-warnings" }; + if(no_video) + args.push_back("-x"); + args.insert(args.end(), { "--", url, nullptr }); + if(exec_program(args.data(), accumulate_string, &json_str) != 0) + return false; + + Json::Value result; + Json::CharReaderBuilder json_builder; + std::unique_ptr json_reader(json_builder.newCharReader()); + std::string json_errors; + if(!json_reader->parse(json_str.data(), json_str.data() + json_str.size(), &result, &json_errors)) { + fprintf(stderr, "Failed to json response, error: %s\n", json_errors.c_str()); + return false; } - program->set_go_to_previous_page(); - return PluginResult::OK; - } - static void add_items(BodyItems &items) { - items.push_back(BodyItem::create("No")); - items.push_back(BodyItem::create("Yes")); - } - private: - FileManagerPage *file_manager_page; - bool *file_overwrite; - std::string title; - }; + const Json::Value &title_json = result["title"]; + const Json::Value &ext_json = result["ext"]; + if(title_json.isString()) + filename = title_json.asString(); - void Program::download_page(const char *url, bool download_use_youtube_dl) { - bool file_overwrite = true; - FileSelectionHandler overwrite_confirm_handler = [this, &file_overwrite](FileManagerPage *file_manager_page, const std::filesystem::path &path) { - file_overwrite = true; - std::vector tabs; - if(std::filesystem::exists(path)) { - auto body = create_body(); - ConfirmationPage::add_items(body->items); - tabs.push_back(Tab{ std::move(body), std::make_unique(this, file_manager_page, &file_overwrite, "Are you sure you want to overwrite " + path.string() + "?"), nullptr }); + if(ext_json.isString()) { + if(ext_json.asCString()[0] != '.' && (filename.empty() || filename.back() != '.')) + filename += "."; + filename += ext_json.asString(); } - return tabs; - }; - auto file_manager_page = std::make_unique(this, FILE_MANAGER_MIME_TYPE_ALL, std::move(overwrite_confirm_handler), true, "Where do you want to save the file? Current directory: "); - file_manager_page->set_current_directory(get_home_dir().data); - auto file_manager_body = create_body(); - file_manager_page->get_files_in_directory(file_manager_body->items); - std::vector file_manager_tabs; - file_manager_tabs.push_back(Tab{std::move(file_manager_body), std::move(file_manager_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + return !filename.empty(); + }); - selected_files.clear(); - page_loop(file_manager_tabs); + if(task_result == TaskResult::CANCEL) { + exit_code = 1; + return; + } - if(!window.isOpen() || selected_files.empty() || !file_overwrite) { + std::string output_filepath = file_save_page(filename); + if(!window.isOpen() || output_filepath.empty()) { exit_code = 1; return; } @@ -5998,7 +5998,6 @@ namespace QuickMedia { } window.setPosition(sf::Vector2i(focused_monitor_center.x - window_size.x * 0.5f, focused_monitor_center.y - window_size.y * 0.5f)); - std::string output_filepath = selected_files[0]; std::string output_filepath_s = output_filepath; char *output_dir = dirname(output_filepath_s.data()); if(create_directory_recursive(output_dir) != 0) { @@ -6022,7 +6021,7 @@ namespace QuickMedia { sf::Text progress_text("0kb/Unknown", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(20.0f * get_ui_scale())); sf::Text status_text("Downloading", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(20.0f * get_ui_scale())); - sf::Text filename_text(get_filename(output_filepath.c_str()), *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14.0f * get_ui_scale())); + sf::Text filename_text(filename.c_str(), *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14.0f * get_ui_scale())); filename_text.setFillColor(sf::Color(179, 179, 179)); sf::Text download_speed_text("0 bytes/s", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14.0f * get_ui_scale())); download_speed_text.setFillColor(sf::Color(179, 179, 179)); @@ -6032,7 +6031,7 @@ namespace QuickMedia { std::unique_ptr downloader; if(download_use_youtube_dl) - downloader = std::make_unique(url, output_filepath, force_no_video); + downloader = std::make_unique(url, output_filepath, no_video); else downloader = std::make_unique(url, output_filepath); @@ -6139,4 +6138,197 @@ namespace QuickMedia { } exit(exit_code); } + + class ConfirmationPage : public Page { + public: + ConfirmationPage(Program *program, bool *file_overwrite, const std::string &title) : Page(program), file_overwrite(file_overwrite), title(title) {} + const char* get_title() const override { return title.c_str(); } + PluginResult submit(const std::string &title, const std::string&, std::vector&) override { + if(title == "Yes") + *file_overwrite = true; + else + *file_overwrite = false; + + program->set_go_to_previous_page(); + return PluginResult::OK; + } + + static void add_items(BodyItems &items) { + items.push_back(BodyItem::create("No")); + items.push_back(BodyItem::create("Yes")); + } + private: + bool *file_overwrite; + std::string title; + }; + + std::string Program::file_save_page(const std::string &filename) { + sf::Vector2f body_pos; + sf::Vector2f body_size; + bool redraw = true; + sf::Event event; + + auto file_manager_page = std::make_unique(this); + std::string home_dir = get_home_dir().data; + file_manager_page->set_current_directory(home_dir); + auto file_manager_body = create_body(); + file_manager_page->get_files_in_directory(file_manager_body->items); + auto search_bar = create_search_bar("Search...", SEARCH_DELAY_FILTER); + + Tabs ui_tabs(&rounded_rectangle_shader); + const int tab_path_index = ui_tabs.add_tab(home_dir); + + search_bar->onTextUpdateCallback = [&file_manager_body](const std::string &text) { + file_manager_body->filter_search_fuzzy(text); + file_manager_body->select_first_item(); + }; + + search_bar->onTextSubmitCallback = [this, &search_bar, &file_manager_body, &file_manager_page, &ui_tabs, tab_path_index](const std::string&) { + if(sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl)) + return; + + BodyItem *selected = file_manager_body->get_selected(); + if(!selected) + return; + + std::vector new_tabs; + TaskResult task_result = run_task_with_loading_screen([selected, &file_manager_page, &new_tabs]() { + return file_manager_page->submit(selected->get_title(), selected->url, new_tabs) == PluginResult::OK; + }); + + if(task_result == TaskResult::TRUE) { + if(!new_tabs.empty()) { + file_manager_body->clear_thumbnails(); + file_manager_body->items = std::move(new_tabs[0].body->items); + } + } else if(task_result == TaskResult::FALSE) { + show_notification("QuickMedia", "Failed to change directory", Urgency::CRITICAL); + } + + search_bar->clear(); + ui_tabs.set_text(tab_path_index, file_manager_page->get_current_directory().string()); + }; + + const float bottom_panel_padding = 10.0f; + const float bottom_panel_spacing = 10.0f; + + Button cancel_button("Cancel", FontLoader::get_font(FontLoader::FontType::LATIN), 16, 100.0f, &rounded_rectangle_shader, get_ui_scale()); + cancel_button.set_background_color(sf::Color(104, 2, 2)); + + Button save_button("Save", FontLoader::get_font(FontLoader::FontType::LATIN), 16, 100.0f, &rounded_rectangle_shader, get_ui_scale()); + save_button.set_background_color(sf::Color(35, 35, 236)); + + sf::Text file_name_label("File name:", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(16.0f * get_ui_scale())); + + Entry file_name_entry("", &rounded_rectangle_shader); + file_name_entry.set_text(filename); + file_name_entry.set_single_line(true); + file_name_entry.set_editable(false); + + sf::RectangleShape bottom_panel_background; + bottom_panel_background.setFillColor(sf::Color(33, 37, 44)); + + auto save_file = [this, &file_name_entry, &file_manager_page]() -> std::string { + auto u8 = file_name_entry.get_text().toUtf8(); + std::string *filename = (std::string*)&u8; + + Path filename_full_path = file_manager_page->get_current_directory().string(); + filename_full_path.join(*filename); + + if(filename->empty()) { + show_notification("QuickMedia", "The file name can't be empty", Urgency::CRITICAL); + } else if(*filename == "." || *filename == ".." || filename->find('/') != std::string::npos) { + show_notification("QuickMedia", "Invalid file name. File can't be ., .. or contain /", Urgency::CRITICAL); + } else if(filename->size() >= 255 || filename_full_path.data.size() >= 4096) { + show_notification("QuickMedia", "The file name has to be less than 255 characters and the full path has to be less than 4096 characters", Urgency::CRITICAL); + } else { + if(std::filesystem::exists(filename_full_path.data)) { + bool overwrite = false; + std::vector tabs; + auto body = create_body(); + ConfirmationPage::add_items(body->items); + tabs.push_back(Tab{ std::move(body), std::make_unique(this, &overwrite, "Are you sure you want to overwrite " + filename_full_path.data + "?"), nullptr }); + page_loop(tabs); + if(overwrite) + return std::move(filename_full_path.data); + } else { + return std::move(filename_full_path.data); + } + } + + return ""; + }; + + while (window.isOpen()) { + while (window.pollEvent(event)) { + if(file_manager_body->on_event(window, event, !file_name_entry.is_editable())) + idle_active_handler(); + else + event_idle_handler(event); + + search_bar->on_event(event); + if(cancel_button.on_event(event) & BUTTON_EVENT_CLICKED) + return ""; + if(save_button.on_event(event) & BUTTON_EVENT_CLICKED) { + std::string save_path = save_file(); + if(!save_path.empty()) + return save_path; + } + file_name_entry.process_event(event); + + if(event.type == sf::Event::Resized) { + window_size.x = event.size.width; + window_size.y = event.size.height; + sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); + window.setView(sf::View(visible_area)); + redraw = true; + idle_active_handler(); + } else if(event.type == sf::Event::GainedFocus) { + redraw = true; + } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Tab) { + file_name_entry.set_editable(!file_name_entry.is_editable()); + search_bar->set_editable(!file_name_entry.is_editable()); + } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Enter && event.key.control) { + std::string save_path = save_file(); + if(!save_path.empty()) + return save_path; + } + } + + update_idle_state(); + handle_window_close(); + search_bar->update(); + + if(redraw) { + redraw = false; + get_body_dimensions(window_size, search_bar.get(), body_pos, body_size); + body_pos.y += std::floor(10.0f * get_ui_scale()) + Tabs::get_height(); + body_size.y -= std::floor(10.0f * get_ui_scale()) + Tabs::get_height(); + save_button.set_position(window_size - sf::Vector2f(save_button.get_width(), save_button.get_height()) - sf::Vector2f(bottom_panel_padding, bottom_panel_padding)); + cancel_button.set_position(save_button.get_position() - sf::Vector2f(cancel_button.get_width() + bottom_panel_spacing, 0.0f)); + file_name_label.setPosition(sf::Vector2f(bottom_panel_spacing, std::floor(window_size.y - bottom_panel_padding - file_name_entry.get_height() * 0.5f - file_name_label.getLocalBounds().height * 0.5f - 5.0f * get_ui_scale()))); + file_name_entry.set_position(sf::Vector2f(file_name_label.getPosition().x + file_name_label.getLocalBounds().width + bottom_panel_spacing, window_size.y - file_name_entry.get_height() - bottom_panel_padding)); + file_name_entry.set_max_width(std::floor(cancel_button.get_position().x - bottom_panel_spacing - file_name_label.getLocalBounds().width - bottom_panel_spacing - bottom_panel_spacing)); + bottom_panel_background.setPosition(0.0f, window_size.y - std::floor(bottom_panel_padding * 2.0f + file_name_entry.get_height())); + bottom_panel_background.setSize(sf::Vector2f(window_size.x, std::floor(bottom_panel_padding * 2.0f + file_name_entry.get_height()))); + } + + window.clear(back_color); + + ui_tabs.draw(window, sf::Vector2f(0.0f, search_bar->getBottomWithoutShadow()), window_size.x); + search_bar->draw(window, window_size, true); + + file_manager_body->draw(window, body_pos, body_size - sf::Vector2f(0.0f, bottom_panel_background.getSize().y)); + + window.draw(bottom_panel_background); + window.draw(file_name_label); + cancel_button.draw(window); + save_button.draw(window); + file_name_entry.draw(window); + + window.display(); + } + + return ""; + } } -- cgit v1.2.3