Compare commits
10 Commits
4061c21e55
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e36e074d0a | |||
|
|
a4c706d327 | ||
|
|
1880278487 | ||
|
|
57d96cab71 | ||
|
|
73649a48e6 | ||
|
|
e5dca6cf34 | ||
|
|
ea80038121 | ||
|
|
740cb023a8 | ||
|
|
b9cb5315dc | ||
|
|
a7505b4073 |
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
39
README.md
Normal file → Executable file
39
README.md
Normal file → Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
Простой файловый менеджер, навеянный идеями ranger.
|
||||||
|
|
||||||
|
Автор никоим образом не претендует на чистоту реализации и верность исполнения.
|
||||||
|
Так что, если вы думаете, что можете сделать это по-другому, то, пожалуйста, сделайте это.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
На данном этапе проект запускается путем клонирования этого репозитория и сборки локально с использованием
|
||||||
|
установленной системы сборки meson.
|
||||||
|
|
||||||
|
**Шаги:**
|
||||||
|
1. установите мезон
|
||||||
|
2. клонируйте репозиторий
|
||||||
|
3. запустите: сборка установки мезона
|
||||||
|
4. запустите скрипт: . run
|
||||||
|
|
||||||
|
Особое внимание нужно обратить на зависимость try_engine. Это самописный движок на котором и написан данный менеджер.
|
||||||
|
В случае если окно запускается, а отображения информации нет - причина одна, в движке зашито слишком много шрифтов и
|
||||||
|
ваша видеокарта не очень это оценила.
|
||||||
|
|
||||||
|
Варианта два. Первое залезть в исходный код и переписать так, как вам кажется верным. Второе - поменять свою старую видеокарту на что-то
|
||||||
|
новее. В любом случае развлекайтесь, как вашей душе угодно.
|
||||||
|
|
||||||
|
Так же просмотрите файл meson.build лежащий в корне проекта, в нем указаны необходимые зависимости для работы. Их нужно установить в систему.
|
||||||
|
|
||||||
|
Цыфра v2 - означает, что это вторая реализация. Первая, была написана с использованием библиотеки ncurcess и находится где-то в аналах
|
||||||
|
данного сайта-хранилища. так что кому нравится это консольное дело смело начинайте с ним бдсмится.
|
||||||
|
|
||||||
|
Я, возможно, переодически буду дополнять данное чудо-творение разноообразными фишками, чистить свой не менее чудесный код от слишком заумных его вариаций, в общем
|
||||||
|
развивать и приводить это в более доступный и красивый вид. Но конкретных сроков и графиков данных работ не стоит и не будет стоять.
|
||||||
|
Чтобы эти слова не значили!
|
||||||
|
|
||||||
|
Ближайшие работы
|
||||||
|
- предпросмотр видео
|
||||||
|
- просмотр кода из файлов
|
||||||
|
- мультивыбор и действия над выбранными файлами
|
||||||
|
- читска кода и тестирование
|
||||||
|
|
||||||
|
PS: проект закрыт. Я открыл для себя yazi
|
||||||
|
|||||||
0
bin/main.cpp
Normal file → Executable file
0
bin/main.cpp
Normal file → Executable file
0
bin/meson.build
Normal file → Executable file
0
bin/meson.build
Normal file → Executable file
BIN
img_logo.png
Executable file
BIN
img_logo.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 406 KiB |
9
meson.build
Normal file → Executable file
9
meson.build
Normal file → Executable file
@@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'rrr.v2',
|
'rrr.v2',
|
||||||
'cpp',
|
'cpp',
|
||||||
version : run_command('jq', '-r', '.version', join_paths(meson.source_root(), 'props.json'), check: true).stdout().strip(),
|
version : '2.0.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
'warning_level=1',
|
'warning_level=1',
|
||||||
'optimization=3',
|
'optimization=3',
|
||||||
@@ -26,12 +26,7 @@ add_project_arguments (
|
|||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
#args = ['-lglfw', '-ldl', '-lGL', '-lpthread', '-lX11', '-lXxf86vm', '-lXrandr', '-lXi']
|
args = []
|
||||||
args = [
|
|
||||||
'-lopenal',
|
|
||||||
'-lsndfile',
|
|
||||||
'-lmpg123',
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
dependency('TBB'),
|
dependency('TBB'),
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
{"version": "1.0.0"}
|
|
||||||
|
|
||||||
@@ -11,3 +11,4 @@ if [[ -z "$1" ]]; then
|
|||||||
else
|
else
|
||||||
meson test $1 -C build
|
meson test $1 -C build
|
||||||
fi
|
fi
|
||||||
|
|
||||||
0
src/meson.build
Normal file → Executable file
0
src/meson.build
Normal file → Executable file
1
src/rrr/buffer/buffer.hpp
Normal file → Executable file
1
src/rrr/buffer/buffer.hpp
Normal file → Executable file
@@ -45,4 +45,3 @@ namespace rrr
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
src/rrr/content/content.cpp
Normal file → Executable file
24
src/rrr/content/content.cpp
Normal file → Executable file
@@ -5,6 +5,7 @@
|
|||||||
#include "logger/logger.hpp"
|
#include "logger/logger.hpp"
|
||||||
|
|
||||||
#include "buffer/buffer.hpp"
|
#include "buffer/buffer.hpp"
|
||||||
|
#include "utils/func.hpp"
|
||||||
|
|
||||||
namespace rrr
|
namespace rrr
|
||||||
{
|
{
|
||||||
@@ -50,14 +51,14 @@ namespace rrr
|
|||||||
set_history_cursor_position();
|
set_history_cursor_position();
|
||||||
f = his.data.at(history_cursor_position);
|
f = his.data.at(history_cursor_position);
|
||||||
}
|
}
|
||||||
catch(...) { hack::error()("Dont set history"); }
|
catch(...) {}
|
||||||
break;
|
break;
|
||||||
case TYPE_WIN::NAVIGATION:
|
case TYPE_WIN::NAVIGATION:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
f = nav.data.at(navigation_cursor_position);
|
f = nav.data.at(navigation_cursor_position);
|
||||||
}
|
}
|
||||||
catch(...) { hack::error()("Dont set navigation"); }
|
catch(...) {}
|
||||||
break;
|
break;
|
||||||
case TYPE_WIN::PREVIEW:
|
case TYPE_WIN::PREVIEW:
|
||||||
try
|
try
|
||||||
@@ -65,7 +66,7 @@ namespace rrr
|
|||||||
set_preview_cursor_position();
|
set_preview_cursor_position();
|
||||||
f = prev.data.at(preview_cursor_position);
|
f = prev.data.at(preview_cursor_position);
|
||||||
}
|
}
|
||||||
catch(...) { hack::error()("Dont set preview"); }
|
catch(...) {}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,16 +188,17 @@ namespace rrr
|
|||||||
void content::create_file(std::string filename)
|
void content::create_file(std::string filename)
|
||||||
{
|
{
|
||||||
std::string cmd;
|
std::string cmd;
|
||||||
|
auto pwd = func::sheilding(PWD);
|
||||||
|
|
||||||
if (filename.find("/") != std::string::npos)
|
if (filename.find("/") != std::string::npos)
|
||||||
{
|
{
|
||||||
if (filename.at(filename.size() - 1) == '/') cmd = "mkdir -p " + std::string(PWD / filename);
|
if (filename.at(filename.size() - 1) == '/') cmd = "mkdir -p " + std::string(pwd / filename);
|
||||||
else cmd = "mkdir -p " + std::string(PWD / std::filesystem::path(filename).parent_path())
|
else cmd = "mkdir -p " + std::string(pwd / std::filesystem::path(filename).parent_path())
|
||||||
+ " && touch " + std::string(PWD / filename);
|
+ " && touch " + std::string(pwd / filename);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmd = "touch " + std::string(PWD / filename);
|
cmd = "touch " + std::string(pwd / filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
hack::utils::unix_cmd(cmd);
|
hack::utils::unix_cmd(cmd);
|
||||||
@@ -215,7 +217,7 @@ namespace rrr
|
|||||||
new_name = old_name.parent_path() / std::filesystem::path(new_name);
|
new_name = old_name.parent_path() / std::filesystem::path(new_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
hack::utils::unix_cmd("mv " + old_name.string() + " " + new_name.string());
|
hack::utils::unix_cmd("mv " + func::sheilding(old_name).string() + " " + func::sheilding(new_name).string());
|
||||||
nav.fill(PWD);
|
nav.fill(PWD);
|
||||||
|
|
||||||
tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
|
tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
|
||||||
@@ -245,7 +247,7 @@ namespace rrr
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string cmd = std::filesystem::is_directory(f) ? "cp -R " : "cp ";
|
std::string cmd = std::filesystem::is_directory(f) ? "cp -R " : "cp ";
|
||||||
hack::utils::unix_cmd(cmd + f.string() + " " + target.string());
|
hack::utils::unix_cmd(cmd + func::sheilding(f).string() + " " + func::sheilding(target).string());
|
||||||
nav.fill(PWD);
|
nav.fill(PWD);
|
||||||
|
|
||||||
check_cursor_position();
|
check_cursor_position();
|
||||||
@@ -253,7 +255,7 @@ namespace rrr
|
|||||||
|
|
||||||
void content::delete_file(file f)
|
void content::delete_file(file f)
|
||||||
{
|
{
|
||||||
std::string cmd = "delete " + f.path.string();
|
std::string cmd = "delete " + func::sheilding(f.path).string();
|
||||||
hack::utils::unix_cmd(cmd);
|
hack::utils::unix_cmd(cmd);
|
||||||
nav.fill(PWD);
|
nav.fill(PWD);
|
||||||
increment_position(-1);
|
increment_position(-1);
|
||||||
@@ -276,7 +278,7 @@ namespace rrr
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string cmd = "mv ";
|
std::string cmd = "mv ";
|
||||||
hack::utils::unix_cmd(cmd + f.string() + " " + target.string());
|
hack::utils::unix_cmd(cmd + func::sheilding(f).string() + " " + func::sheilding(target).string());
|
||||||
nav.fill(PWD);
|
nav.fill(PWD);
|
||||||
|
|
||||||
tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
|
tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
|
||||||
|
|||||||
3
src/rrr/content/content.hpp
Normal file → Executable file
3
src/rrr/content/content.hpp
Normal file → Executable file
@@ -51,13 +51,12 @@ namespace rrr
|
|||||||
int navigation_cursor_position = 0;
|
int navigation_cursor_position = 0;
|
||||||
int preview_cursor_position = 0;
|
int preview_cursor_position = 0;
|
||||||
int history_cursor_position = 0;
|
int history_cursor_position = 0;
|
||||||
|
file navigation_selected_file;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_history_cursor_position();
|
void set_history_cursor_position();
|
||||||
void set_preview_cursor_position();
|
void set_preview_cursor_position();
|
||||||
void check_cursor_position();
|
void check_cursor_position();
|
||||||
|
|
||||||
file navigation_selected_file;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/rrr/content/file/file.cpp
Normal file → Executable file
0
src/rrr/content/file/file.cpp
Normal file → Executable file
0
src/rrr/content/file/file.hpp
Normal file → Executable file
0
src/rrr/content/file/file.hpp
Normal file → Executable file
0
src/rrr/content/history/history.cpp
Normal file → Executable file
0
src/rrr/content/history/history.cpp
Normal file → Executable file
0
src/rrr/content/history/history.hpp
Normal file → Executable file
0
src/rrr/content/history/history.hpp
Normal file → Executable file
0
src/rrr/content/navigation/navigation.cpp
Normal file → Executable file
0
src/rrr/content/navigation/navigation.cpp
Normal file → Executable file
0
src/rrr/content/navigation/navigation.hpp
Normal file → Executable file
0
src/rrr/content/navigation/navigation.hpp
Normal file → Executable file
0
src/rrr/content/preview/preview.cpp
Normal file → Executable file
0
src/rrr/content/preview/preview.cpp
Normal file → Executable file
0
src/rrr/content/preview/preview.hpp
Normal file → Executable file
0
src/rrr/content/preview/preview.hpp
Normal file → Executable file
2
src/rrr/layers/gui/browser/history/history.cpp
Normal file → Executable file
2
src/rrr/layers/gui/browser/history/history.cpp
Normal file → Executable file
@@ -52,7 +52,7 @@ namespace rrr::layers::gui
|
|||||||
TR_PUSH_FONT(SEMI_BOLD, 18);
|
TR_PUSH_FONT(SEMI_BOLD, 18);
|
||||||
ImGui::TextUnformatted(">");
|
ImGui::TextUnformatted(">");
|
||||||
ImGui::SameLine(22.f);
|
ImGui::SameLine(22.f);
|
||||||
current_position = it - data->begin();
|
current_position = std::distance(data->begin(), it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
0
src/rrr/layers/gui/browser/history/history.hpp
Normal file → Executable file
0
src/rrr/layers/gui/browser/history/history.hpp
Normal file → Executable file
247
src/rrr/layers/gui/browser/navigation/navigation.cpp
Normal file → Executable file
247
src/rrr/layers/gui/browser/navigation/navigation.cpp
Normal file → Executable file
@@ -1,6 +1,7 @@
|
|||||||
#include "navigation.hpp"
|
#include "navigation.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#include "try_engine/event/event_classificator.hpp"
|
#include "try_engine/event/event_classificator.hpp"
|
||||||
#include "utils/types.hpp"
|
#include "utils/types.hpp"
|
||||||
@@ -50,7 +51,7 @@ namespace rrr::layers::gui
|
|||||||
|
|
||||||
push_style(item);
|
push_style(item);
|
||||||
|
|
||||||
if (selected_file.path == item.path)
|
if (!selected_file.path.empty() && selected_file.path == item.path)
|
||||||
{
|
{
|
||||||
if (selected_file.is_hidden)
|
if (selected_file.is_hidden)
|
||||||
TR_PUSH_FONT(SEMI_BOLD_ITALIC, 18);
|
TR_PUSH_FONT(SEMI_BOLD_ITALIC, 18);
|
||||||
@@ -59,9 +60,8 @@ namespace rrr::layers::gui
|
|||||||
|
|
||||||
ImGui::TextUnformatted(">");
|
ImGui::TextUnformatted(">");
|
||||||
ImGui::SameLine(22.f);
|
ImGui::SameLine(22.f);
|
||||||
// HERE
|
|
||||||
// нужно использовать std::distance
|
current_position = std::distance(data->begin(), it);
|
||||||
current_position = it - data->begin();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -200,28 +200,28 @@ namespace rrr::layers::gui
|
|||||||
if (shift)
|
if (shift)
|
||||||
{
|
{
|
||||||
// удаление
|
// удаление
|
||||||
if (shift && key.get_keycode() == try_engine::key::D)
|
if (key.get_keycode() == try_engine::key::D)
|
||||||
{
|
{
|
||||||
em->execute(types::event_type::SHOW_DELETE_ONE_FILE_DIALOG, selected_file);
|
em->execute(types::event_type::SHOW_DELETE_ONE_FILE_DIALOG, selected_file);
|
||||||
freeze = true;
|
freeze = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// создание файла/директории
|
// создание файла/директории
|
||||||
if (shift && key.get_keycode() == try_engine::key::A)
|
if (key.get_keycode() == try_engine::key::A)
|
||||||
{
|
{
|
||||||
em->execute(types::event_type::SHOW_CREATE_FILE_DIALOG, nullptr);
|
em->execute(types::event_type::SHOW_CREATE_FILE_DIALOG, nullptr);
|
||||||
freeze = true;
|
freeze = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// переименование файла/директории
|
// переименование файла/директории
|
||||||
if (shift && key.get_keycode() == try_engine::key::R)
|
if (key.get_keycode() == try_engine::key::R)
|
||||||
{
|
{
|
||||||
em->execute(types::event_type::SHOW_RENAME_FILE_DIALOG, selected_file);
|
em->execute(types::event_type::SHOW_RENAME_FILE_DIALOG, selected_file);
|
||||||
freeze = true;
|
freeze = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// перемещение в конец списка
|
// перемещение в конец списка
|
||||||
if (shift && key.get_keycode() == try_engine::key::G)
|
if (key.get_keycode() == try_engine::key::G)
|
||||||
{
|
{
|
||||||
cnt->increment_position(data->size());
|
cnt->increment_position(data->size());
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
@@ -231,110 +231,147 @@ namespace rrr::layers::gui
|
|||||||
}
|
}
|
||||||
|
|
||||||
// вставка из single_buffer
|
// вставка из single_buffer
|
||||||
if (shift && key.get_keycode() == try_engine::key::P)
|
if (key.get_keycode() == try_engine::key::P)
|
||||||
{
|
{
|
||||||
paste_from_copy();
|
paste_from_copy();
|
||||||
paste_from_cut();
|
paste_from_cut();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
// перемещение на несколько позиций вниз
|
||||||
|
if (key.get_keycode() == try_engine::key::J)
|
||||||
|
{
|
||||||
|
cnt->increment_position((data->size() - current_position) / 4);
|
||||||
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
|
em->execute(types::event_type::NAVIGATION_DOWN, nullptr);
|
||||||
|
set_delta(MOVE_DIRECTION::DOWN);
|
||||||
|
set_scroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// перемещение на несколько позиций вверх
|
||||||
|
if (key.get_keycode() == try_engine::key::K)
|
||||||
|
{
|
||||||
|
cnt->increment_position(current_position - current_position / 4);
|
||||||
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
|
em->execute(types::event_type::NAVIGATION_UP, nullptr);
|
||||||
|
set_delta(MOVE_DIRECTION::UP);
|
||||||
|
set_scroll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (key.get_keycode() == try_engine::key::J)
|
|
||||||
{
|
{
|
||||||
cnt->increment_position(STEP_DOWN);
|
if (key.get_keycode() == try_engine::key::J)
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
{
|
||||||
em->execute(types::event_type::NAVIGATION_DOWN, nullptr);
|
cnt->increment_position(STEP_DOWN);
|
||||||
set_delta(MOVE_DIRECTION::DOWN);
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
}
|
em->execute(types::event_type::NAVIGATION_DOWN, nullptr);
|
||||||
|
set_delta(MOVE_DIRECTION::DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
if (key.get_keycode() == try_engine::key::K)
|
if (key.get_keycode() == try_engine::key::K)
|
||||||
{
|
{
|
||||||
cnt->increment_position(STEP_UP);
|
cnt->increment_position(STEP_UP);
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
em->execute(types::event_type::NAVIGATION_UP, nullptr);
|
em->execute(types::event_type::NAVIGATION_UP, nullptr);
|
||||||
set_delta(MOVE_DIRECTION::UP);
|
set_delta(MOVE_DIRECTION::UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.get_keycode() == try_engine::key::H)
|
if (key.get_keycode() == try_engine::key::H)
|
||||||
{
|
{
|
||||||
cnt->navigation_left();
|
cnt->navigation_left();
|
||||||
data = cnt->get(TYPE_WIN::NAVIGATION);
|
data = cnt->get(TYPE_WIN::NAVIGATION);
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
em->execute(types::event_type::NAVIGATION_LEFT, nullptr);
|
em->execute(types::event_type::NAVIGATION_LEFT, nullptr);
|
||||||
set_scroll();
|
set_scroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.get_keycode() == try_engine::key::L)
|
if (key.get_keycode() == try_engine::key::ENTER)
|
||||||
{
|
{
|
||||||
cnt->navigation_right();
|
if (std::filesystem::is_directory(selected_file.path))
|
||||||
data = cnt->get(TYPE_WIN::NAVIGATION);
|
{
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
cnt->navigation_right();
|
||||||
em->execute(types::event_type::NAVIGATION_RIGHT, nullptr);
|
data = cnt->get(TYPE_WIN::NAVIGATION);
|
||||||
set_scroll();
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
}
|
em->execute(types::event_type::NAVIGATION_RIGHT, nullptr);
|
||||||
|
set_scroll();
|
||||||
// двойное нажатие перемещение в начало списка
|
}
|
||||||
if (key.get_keycode() == try_engine::key::G)
|
|
||||||
{
|
|
||||||
++g_coutn;
|
|
||||||
if (g_coutn != 2) return;
|
|
||||||
cnt->increment_position(-data->size());
|
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
|
||||||
em->execute(types::event_type::NAVIGATION_UP, nullptr);
|
|
||||||
set_delta(MOVE_DIRECTION::UP);
|
|
||||||
set_scroll();
|
|
||||||
g_coutn = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// копирование/добавление в буфер одного файла вместо другого
|
|
||||||
// когда в буфере что-то есть и мы на томже файле жмем кнопку еще раз
|
|
||||||
// то происходит очищение буфера
|
|
||||||
if (key.get_keycode() == try_engine::key::C)
|
|
||||||
{
|
|
||||||
// проверяем а может это файл отмечен на вырезание
|
|
||||||
if (!buffers::get_instance().single_cut_buffer.empty())
|
|
||||||
buffers::get_instance().single_cut_buffer.clear();
|
|
||||||
|
|
||||||
if (buffers::get_instance().single_copy_buffer.empty())
|
|
||||||
buffers::get_instance().single_copy_buffer[1] = selected_file;
|
|
||||||
else
|
|
||||||
if (selected_file.path == buffers::get_instance().single_copy_buffer[1].path)
|
|
||||||
buffers::get_instance().single_copy_buffer.clear();
|
|
||||||
else
|
else
|
||||||
buffers::get_instance().single_copy_buffer[1] = selected_file;
|
{
|
||||||
}
|
run_app();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (key.get_keycode() == try_engine::key::X)
|
if (key.get_keycode() == try_engine::key::L)
|
||||||
{
|
{
|
||||||
// проверяем а может это файл отмечен на копирование
|
if (!std::filesystem::is_directory(selected_file.path)) return;
|
||||||
if (!buffers::get_instance().single_copy_buffer.empty())
|
cnt->navigation_right();
|
||||||
buffers::get_instance().single_copy_buffer.clear();
|
data = cnt->get(TYPE_WIN::NAVIGATION);
|
||||||
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
|
em->execute(types::event_type::NAVIGATION_RIGHT, nullptr);
|
||||||
|
set_scroll();
|
||||||
|
}
|
||||||
|
|
||||||
if (buffers::get_instance().single_cut_buffer.empty())
|
// двойное нажатие перемещение в начало списка
|
||||||
buffers::get_instance().single_cut_buffer[1] = selected_file;
|
if (key.get_keycode() == try_engine::key::G)
|
||||||
else
|
{
|
||||||
if (selected_file.path == buffers::get_instance().single_cut_buffer[1].path)
|
++g_coutn;
|
||||||
|
if (g_coutn != 2) return;
|
||||||
|
cnt->increment_position(-data->size());
|
||||||
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
|
em->execute(types::event_type::NAVIGATION_UP, nullptr);
|
||||||
|
set_delta(MOVE_DIRECTION::UP);
|
||||||
|
set_scroll();
|
||||||
|
g_coutn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// копирование/добавление в буфер одного файла вместо другого
|
||||||
|
// когда в буфере что-то есть и мы на томже файле жмем кнопку еще раз
|
||||||
|
// то происходит очищение буфера
|
||||||
|
if (key.get_keycode() == try_engine::key::C)
|
||||||
|
{
|
||||||
|
// проверяем а может это файл отмечен на вырезание
|
||||||
|
if (!buffers::get_instance().single_cut_buffer.empty())
|
||||||
buffers::get_instance().single_cut_buffer.clear();
|
buffers::get_instance().single_cut_buffer.clear();
|
||||||
|
|
||||||
|
if (buffers::get_instance().single_copy_buffer.empty())
|
||||||
|
buffers::get_instance().single_copy_buffer[1] = selected_file;
|
||||||
else
|
else
|
||||||
|
if (selected_file.path == buffers::get_instance().single_copy_buffer[1].path)
|
||||||
|
buffers::get_instance().single_copy_buffer.clear();
|
||||||
|
else
|
||||||
|
buffers::get_instance().single_copy_buffer[1] = selected_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.get_keycode() == try_engine::key::X)
|
||||||
|
{
|
||||||
|
// проверяем а может это файл отмечен на копирование
|
||||||
|
if (!buffers::get_instance().single_copy_buffer.empty())
|
||||||
|
buffers::get_instance().single_copy_buffer.clear();
|
||||||
|
|
||||||
|
if (buffers::get_instance().single_cut_buffer.empty())
|
||||||
buffers::get_instance().single_cut_buffer[1] = selected_file;
|
buffers::get_instance().single_cut_buffer[1] = selected_file;
|
||||||
|
else
|
||||||
|
if (selected_file.path == buffers::get_instance().single_cut_buffer[1].path)
|
||||||
|
buffers::get_instance().single_cut_buffer.clear();
|
||||||
|
else
|
||||||
|
buffers::get_instance().single_cut_buffer[1] = selected_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// очистка
|
||||||
|
if (key.get_keycode() == try_engine::key::ESCAPE)
|
||||||
|
{
|
||||||
|
buffers::get_instance().single_copy_buffer.clear();
|
||||||
|
buffers::get_instance().single_cut_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// помощь
|
||||||
|
if (key.get_keycode() == try_engine::key::F1)
|
||||||
|
{
|
||||||
|
em->execute(types::event_type::SHOW_HELP_DIALOG, nullptr);
|
||||||
|
freeze = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// очистка
|
detect_file();
|
||||||
if (key.get_keycode() == try_engine::key::ESCAPE)
|
|
||||||
{
|
|
||||||
buffers::get_instance().single_copy_buffer.clear();
|
|
||||||
buffers::get_instance().single_cut_buffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// помощь
|
|
||||||
if (key.get_keycode() == try_engine::key::F1)
|
|
||||||
{
|
|
||||||
em->execute(types::event_type::SHOW_HELP_DIALOG, nullptr);
|
|
||||||
freeze = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
detect_img();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void navigation::released(system_event& e)
|
void navigation::released(system_event& e)
|
||||||
@@ -365,7 +402,7 @@ namespace rrr::layers::gui
|
|||||||
if (current_position >= size) current_position = size - 1;
|
if (current_position >= size) current_position = size - 1;
|
||||||
|
|
||||||
bool is_end = size - current_position <= 10;
|
bool is_end = size - current_position <= 10;
|
||||||
if (cursor_position >= h - 10 && !is_end)
|
if (cursor_position >= h - 10 && !is_end)
|
||||||
{
|
{
|
||||||
++delta;
|
++delta;
|
||||||
cursor_position = h - 10;
|
cursor_position = h - 10;
|
||||||
@@ -396,6 +433,8 @@ namespace rrr::layers::gui
|
|||||||
|
|
||||||
void navigation::set_scroll()
|
void navigation::set_scroll()
|
||||||
{
|
{
|
||||||
|
if (selected_file.path.empty()) return;
|
||||||
|
|
||||||
delta = 0;
|
delta = 0;
|
||||||
current_position = 0;
|
current_position = 0;
|
||||||
cursor_position = 0;
|
cursor_position = 0;
|
||||||
@@ -436,11 +475,27 @@ namespace rrr::layers::gui
|
|||||||
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void navigation::detect_img()
|
void navigation::detect_file()
|
||||||
{
|
{
|
||||||
|
if (selected_file.path.empty()) return;
|
||||||
|
if (std::filesystem::is_directory(selected_file.path)) return;
|
||||||
|
|
||||||
if (selected_file.path.extension() == ".jpg" ||
|
if (selected_file.path.extension() == ".jpg" ||
|
||||||
selected_file.path.extension() == ".jpeg" ||
|
selected_file.path.extension() == ".jpeg" ||
|
||||||
selected_file.path.extension() == ".png")
|
selected_file.path.extension() == ".png")
|
||||||
em->execute(types::event_type::SHOW_IMG, selected_file.path);
|
em->execute(types::event_type::SHOW_IMG, selected_file.path);
|
||||||
|
else
|
||||||
|
em->execute(types::event_type::SHOW_SRC, selected_file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigation::run_app()
|
||||||
|
{
|
||||||
|
// if (selected_file.path.extension() == ".mp3" ||
|
||||||
|
// selected_file.path.extension() == ".mp4" ||
|
||||||
|
// selected_file.path.extension() == ".wave")
|
||||||
|
|
||||||
|
// HERE начинаем тут
|
||||||
|
// сделать запуск разных программ из под rrr
|
||||||
|
system(("kitty --name \"project_rrr_editor\" --directory " + selected_file.path.parent_path().string() + " nv ").c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/rrr/layers/gui/browser/navigation/navigation.hpp
Normal file → Executable file
4
src/rrr/layers/gui/browser/navigation/navigation.hpp
Normal file → Executable file
@@ -68,9 +68,11 @@ namespace rrr::layers::gui
|
|||||||
std::string get_file_content(file&);
|
std::string get_file_content(file&);
|
||||||
void set_delta(MOVE_DIRECTION);
|
void set_delta(MOVE_DIRECTION);
|
||||||
void set_scroll();
|
void set_scroll();
|
||||||
|
void run_app();
|
||||||
void paste_from_copy(); // вставка после копирования
|
void paste_from_copy(); // вставка после копирования
|
||||||
void paste_from_cut(); // вставки после вырезания
|
void paste_from_cut(); // вставки после вырезания
|
||||||
void detect_img();
|
void detect_file(); // определяем не кортинка ли это или определяем не является ли
|
||||||
|
// это что-то типа кода, который можно вывести на просмотр
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
src/rrr/layers/gui/browser/preview/preview.cpp
Normal file → Executable file
30
src/rrr/layers/gui/browser/preview/preview.cpp
Normal file → Executable file
@@ -43,16 +43,22 @@ namespace rrr::layers::gui
|
|||||||
{
|
{
|
||||||
make_media = false;
|
make_media = false;
|
||||||
tx.make();
|
tx.make();
|
||||||
frame = cv::imread(media_path);
|
frame = cv::imread(src_path);
|
||||||
cv::cvtColor(frame, frame, cv::COLOR_BGR2RGBA);
|
cv::cvtColor(frame, frame, cv::COLOR_BGR2RGBA);
|
||||||
tx.bind(frame);
|
tx.bind(frame);
|
||||||
media_show = true;
|
media_show = true;
|
||||||
hack::log()(width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media_show)
|
if (media_show)
|
||||||
tx.draw(ImVec2(pos.x + 830.f, pos.y), ImVec2(width + 850.f, frame.rows / 1.6f));
|
tx.draw(ImVec2(pos.x + 830.f, pos.y), ImVec2(width + 850.f, frame.rows / 1.6f));
|
||||||
|
|
||||||
|
if (src_show)
|
||||||
|
{
|
||||||
|
TR_PUSH_FONT(MEDIUM, 18);
|
||||||
|
ImGui::TextUnformatted(src_data.data());
|
||||||
|
TR_POP_FONT();
|
||||||
|
}
|
||||||
|
|
||||||
for (files::iterator it = begin; it != end; ++it)
|
for (files::iterator it = begin; it != end; ++it)
|
||||||
{
|
{
|
||||||
auto item = *it;
|
auto item = *it;
|
||||||
@@ -68,7 +74,7 @@ namespace rrr::layers::gui
|
|||||||
TR_PUSH_FONT(SEMI_BOLD, 18);
|
TR_PUSH_FONT(SEMI_BOLD, 18);
|
||||||
ImGui::TextUnformatted(">");
|
ImGui::TextUnformatted(">");
|
||||||
ImGui::SameLine(22.f);
|
ImGui::SameLine(22.f);
|
||||||
current_position = it - data->begin();
|
current_position = std::distance(data->begin(), it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -114,7 +120,9 @@ namespace rrr::layers::gui
|
|||||||
case types::event_type::NAVIGATION_DOWN:
|
case types::event_type::NAVIGATION_DOWN:
|
||||||
case types::event_type::NAVIGATION_LEFT:
|
case types::event_type::NAVIGATION_LEFT:
|
||||||
case types::event_type::NAVIGATION_RIGHT:
|
case types::event_type::NAVIGATION_RIGHT:
|
||||||
media_path.clear();
|
src_path.clear();
|
||||||
|
src_data.clear();
|
||||||
|
src_show = false;
|
||||||
media_show = false;
|
media_show = false;
|
||||||
selected_file = cnt->get_selected_file(TYPE_WIN::PREVIEW);
|
selected_file = cnt->get_selected_file(TYPE_WIN::PREVIEW);
|
||||||
cursor_position = 0;
|
cursor_position = 0;
|
||||||
@@ -123,9 +131,21 @@ namespace rrr::layers::gui
|
|||||||
set_scroll();
|
set_scroll();
|
||||||
break;
|
break;
|
||||||
case types::event_type::SHOW_IMG:
|
case types::event_type::SHOW_IMG:
|
||||||
media_path = std::any_cast<std::filesystem::path>(value);
|
src_path = std::any_cast<std::filesystem::path>(value);
|
||||||
make_media = true;
|
make_media = true;
|
||||||
break;
|
break;
|
||||||
|
case types::event_type::SHOW_SRC:
|
||||||
|
src_path = std::any_cast<std::filesystem::path>(value);
|
||||||
|
// HERE
|
||||||
|
// пока не подсвечиваем, для этого нужно видимо написать свой токенайзер
|
||||||
|
// и разбирать всю баш строку на наличие цветов
|
||||||
|
// буз этого пока поживешь, не сломаешься!
|
||||||
|
// src_data = hack::utils::unix_cmd("bat --style=plain --color=always " + src_path.string());
|
||||||
|
if (std::filesystem::is_empty(src_path)) src_data = "no data";
|
||||||
|
else
|
||||||
|
src_data = hack::utils::unix_cmd("cat " + src_path.string());
|
||||||
|
src_show = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/rrr/layers/gui/browser/preview/preview.hpp
Normal file → Executable file
4
src/rrr/layers/gui/browser/preview/preview.hpp
Normal file → Executable file
@@ -42,12 +42,14 @@ namespace rrr::layers::gui
|
|||||||
int delta = 0;
|
int delta = 0;
|
||||||
enum class MOVE_DIRECTION { UP, DOWN };
|
enum class MOVE_DIRECTION { UP, DOWN };
|
||||||
|
|
||||||
std::filesystem::path media_path;
|
|
||||||
try_engine::texture tx;
|
try_engine::texture tx;
|
||||||
cv::Mat frame;
|
cv::Mat frame;
|
||||||
cv::VideoCapture cap;
|
cv::VideoCapture cap;
|
||||||
|
std::filesystem::path src_path;
|
||||||
bool media_show = false;
|
bool media_show = false;
|
||||||
bool make_media = false;
|
bool make_media = false;
|
||||||
|
bool src_show = false;
|
||||||
|
std::string src_data; // данные для показа кода из файла
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ImVec4 dir_color = func::get_IMGUI_color<ImVec4>(91.f, 128.f, 191.f);
|
const ImVec4 dir_color = func::get_IMGUI_color<ImVec4>(91.f, 128.f, 191.f);
|
||||||
|
|||||||
13
src/rrr/layers/gui/dialogs/dialogs.cpp
Normal file → Executable file
13
src/rrr/layers/gui/dialogs/dialogs.cpp
Normal file → Executable file
@@ -142,9 +142,6 @@ namespace rrr::layers::gui
|
|||||||
if (rename_dialog)
|
if (rename_dialog)
|
||||||
rename();
|
rename();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (help_dialog && key.get_keycode() == try_engine::key::F1)
|
|
||||||
cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dialogs::released(system_event& e)
|
void dialogs::released(system_event& e)
|
||||||
@@ -284,7 +281,8 @@ namespace rrr::layers::gui
|
|||||||
|
|
||||||
void dialogs::draw_help_dialog()
|
void dialogs::draw_help_dialog()
|
||||||
{
|
{
|
||||||
height = try_engine::application::get()->get_window()->height() / 2.7f;
|
hack::log()("draw_help_dialog");
|
||||||
|
height = try_engine::application::get()->get_window()->height() / 2.4f;
|
||||||
|
|
||||||
TR_PUSH_FONT(MEDIUM, 16);
|
TR_PUSH_FONT(MEDIUM, 16);
|
||||||
|
|
||||||
@@ -300,18 +298,21 @@ namespace rrr::layers::gui
|
|||||||
ImGui::TextUnformatted(label.data());
|
ImGui::TextUnformatted(label.data());
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::TextUnformatted("Копирование: С / backspace");
|
ImGui::TextUnformatted("Копирование: c / backspace");
|
||||||
ImGui::TextUnformatted("Вставка: P");
|
ImGui::TextUnformatted("Вставка: P");
|
||||||
ImGui::TextUnformatted("Удаление: D");
|
ImGui::TextUnformatted("Удаление: D");
|
||||||
ImGui::TextUnformatted("Переименование: R");
|
ImGui::TextUnformatted("Переименование: R");
|
||||||
ImGui::TextUnformatted("Создание: A");
|
ImGui::TextUnformatted("Создание: A");
|
||||||
|
ImGui::TextUnformatted("Вырезание: x");
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::TextUnformatted("Перемещение в начало: gg");
|
ImGui::TextUnformatted("Перемещение в начало: gg");
|
||||||
ImGui::TextUnformatted("Перемещение в конец: G");
|
ImGui::TextUnformatted("Перемещение в конец: G");
|
||||||
|
ImGui::TextUnformatted("Быстрое перемещение вверх: J");
|
||||||
|
ImGui::TextUnformatted("Быстрое перемещение вниз: K");
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::TextUnformatted("Выход из помощи: F1");
|
ImGui::TextUnformatted("Выход из диалогов: ESCAPE");
|
||||||
|
|
||||||
TR_POP_FONT();
|
TR_POP_FONT();
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/rrr/layers/gui/dialogs/dialogs.hpp
Normal file → Executable file
0
src/rrr/layers/gui/dialogs/dialogs.hpp
Normal file → Executable file
0
src/rrr/meson.build
Normal file → Executable file
0
src/rrr/meson.build
Normal file → Executable file
12
src/rrr/rrr.hpp
Normal file → Executable file
12
src/rrr/rrr.hpp
Normal file → Executable file
@@ -1,3 +1,4 @@
|
|||||||
|
#include "logger/logger.hpp"
|
||||||
#include "try_engine/try_engine.hpp"
|
#include "try_engine/try_engine.hpp"
|
||||||
|
|
||||||
#include "content/content.hpp"
|
#include "content/content.hpp"
|
||||||
@@ -16,9 +17,14 @@ namespace rrr
|
|||||||
public:
|
public:
|
||||||
rrr_impl(std::string app_name) : try_engine::application{ app_name }
|
rrr_impl(std::string app_name) : try_engine::application{ app_name }
|
||||||
{
|
{
|
||||||
// HERE
|
std::string pwd = hack::utils::unix_cmd("pwd");
|
||||||
// убрать это в релизе
|
|
||||||
cnt.set_pwd("/mnt/develop/projects/cpp/rrr/dir_for_tests");
|
// перемещаем все знаки перевода строки в конец строки
|
||||||
|
// т.к. там это есть и это вызывает ошибку
|
||||||
|
auto it = std::remove(pwd.begin(), pwd.end(), '\n');
|
||||||
|
pwd.erase(it, pwd.end());
|
||||||
|
|
||||||
|
cnt.set_pwd(std::filesystem::path(pwd));
|
||||||
cnt.fill();
|
cnt.fill();
|
||||||
};
|
};
|
||||||
~rrr_impl() = default;
|
~rrr_impl() = default;
|
||||||
|
|||||||
22
src/rrr/utils/func.hpp
Normal file → Executable file
22
src/rrr/utils/func.hpp
Normal file → Executable file
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace rrr::func
|
namespace rrr::func
|
||||||
{
|
{
|
||||||
template<typename Color>
|
template<typename Color>
|
||||||
@@ -7,4 +9,24 @@ namespace rrr::func
|
|||||||
{
|
{
|
||||||
return Color(r / 255.f, g / 255.f, b / 255.f, 1.f);
|
return Color(r / 255.f, g / 255.f, b / 255.f, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// для экоранирования путей чтоб unix команды нормально выполнялись
|
||||||
|
// правила тут вилимо будут добавлятся так что эт не конец:(
|
||||||
|
inline std::filesystem::path sheilding(std::filesystem::path PWD)
|
||||||
|
{
|
||||||
|
std::string pwd_local;
|
||||||
|
|
||||||
|
for (char c : PWD.string()) {
|
||||||
|
if (c == ' ')
|
||||||
|
pwd_local += "\\ ";
|
||||||
|
else if (c == '(')
|
||||||
|
pwd_local += "\\(";
|
||||||
|
else if (c == ')')
|
||||||
|
pwd_local += "\\)";
|
||||||
|
else
|
||||||
|
pwd_local += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pwd_local;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/rrr/utils/types.hpp
Normal file → Executable file
3
src/rrr/utils/types.hpp
Normal file → Executable file
@@ -18,7 +18,8 @@ namespace rrr::types
|
|||||||
SHOW_RENAME_FILE_DIALOG,
|
SHOW_RENAME_FILE_DIALOG,
|
||||||
RENAME_FILE,
|
RENAME_FILE,
|
||||||
SHOW_HELP_DIALOG,
|
SHOW_HELP_DIALOG,
|
||||||
SHOW_IMG
|
SHOW_IMG,
|
||||||
|
SHOW_SRC // показываем содержимое файла если это код
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
subprojects/glad.wrap
Normal file → Executable file
0
subprojects/glad.wrap
Normal file → Executable file
0
subprojects/glfw.wrap
Normal file → Executable file
0
subprojects/glfw.wrap
Normal file → Executable file
0
subprojects/glm.wrap
Normal file → Executable file
0
subprojects/glm.wrap
Normal file → Executable file
0
subprojects/gtest.wrap
Normal file → Executable file
0
subprojects/gtest.wrap
Normal file → Executable file
0
subprojects/hack.wrap
Normal file → Executable file
0
subprojects/hack.wrap
Normal file → Executable file
0
subprojects/imgui.wrap
Normal file → Executable file
0
subprojects/imgui.wrap
Normal file → Executable file
0
subprojects/nlohmann_json.wrap
Normal file → Executable file
0
subprojects/nlohmann_json.wrap
Normal file → Executable file
0
subprojects/taglib.wrap
Normal file → Executable file
0
subprojects/taglib.wrap
Normal file → Executable file
0
subprojects/try_engine.wrap
Normal file → Executable file
0
subprojects/try_engine.wrap
Normal file → Executable file
0
tests/meson.build
Normal file → Executable file
0
tests/meson.build
Normal file → Executable file
Reference in New Issue
Block a user