sg
-
Автор темы
- #1
Итак. Приветствую человека, который зашел в мою тему. Сегодня я ликну достаточно интересную штуку, а именно расширенную систему кейбиндов ( как вот в этих ваших неврлуз.цц, оверси.ю ( покойся с миром ) и ванфлюид.тех ). Всё написано с помощью библиотеки ImGui. Я не видел каких-либо похожих тем, и реализация у меня будет (возможно) получше. Базу чита использовал CSGOSimple.
Начать стоит с того, что мы будем использовать, какие структуры и тому подобное.
Но есть небольшая уловочка для любителей попастить. Хоть я и предоставляю готовый код, как вы видите, я не сделал кейбиндов для слайдеров и других каких-нибудь виджетов. Это вам предстоит сделать самим)
А дальше у нас идут определения всех функций.
Вот в общем-то основной код, который вам понадобится. Но надо бы написать ещё функцию, которая будет проверять, нажата ли кнопка и включать или выключать наш Checkbox. Вот простенький код для этих двух режимов
Как-то так мои дорогие друзьяшки. Думаю не буду ставить даже хайд на данную тему, я просто не хочу, чтобы люди использовали кейбинд систему как в лв или прямо в функциях проверяли активна ли кнопка для данного чекбокса. Так что готов к любой критике, надеюсь мой гайд хоть немножко помог пользователям данного форума)
Так же вот вам и SS:
Начать стоит с того, что мы будем использовать, какие структуры и тому подобное.
C++:
// Для определния на что мы нажали ПКМ и какой тип вызывать
enum EHotKeyType : int
{
HOTKEY_CHECKBOX,
HOTKEY_SLIDER,
HOTKEY_NONE
};
// Как будет работать бинд
enum EHotKeyMode : int
{
MODE_TOGGLE = 0,
MODE_HOLD
};
C++:
// Структура нашего бинда специально для чекбокса
struct ImGui_HotKey_Checkbox
{
int id = -1;
std::string name;
std::string label_name;
int key = 0;
int mode = MODE_HOLD;
bool* value = nullptr;
ImGui_HotKey_Checkbox()
{
id = -1;
label_name = "";
key = 0;
}
ImGui_HotKey_Checkbox(int _id, bool* _value)
{
id = _id;
label_name = "";
key = 0;
value = _value;
}
};
C++:
namespace ImGui_HotKeys
{
// Открыто ли меню
inline bool open_menu;
// Позиция для отрисовки меню
inline ImVec2 menu_pos;
// Массив кейбиндов специально для Checkbox
inline std::vector<ImGui_HotKey_Checkbox> checkbox_hotkeys;
inline ImGui_HotKey_Checkbox current_checkbox;
// "Кастомный" Checkbox
bool Checkbox(const char* label, bool* v);
// Combo для отображения кейбиндов под Checkbox
bool Combo_CheckBox(const char* label, int& current_item, std::vector<ImGui_HotKey_Checkbox>& data);
bool Hotkey(const char* label, int& k, const ImVec2& size_arg = ImVec2(0, 0));
void Draw();
}
Но есть небольшая уловочка для любителей попастить. Хоть я и предоставляю готовый код, как вы видите, я не сделал кейбиндов для слайдеров и других каких-нибудь виджетов. Это вам предстоит сделать самим)
А дальше у нас идут определения всех функций.
C++:
bool ImGui_HotKeys::Checkbox(const char* label, bool* v)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
const float square_sz = 14;
const ImVec2 pos = window->DC.CursorPos;
const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, id))
{
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
return false;
}
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held);
if (pressed)
{
*v = !(*v);
ImGui::MarkItemEdited(id);
}
// если курсор наведён и нажата ПКМ
if (hovered && g.IO.MouseClicked[1])
{
open_menu = true;
menu_pos = total_bb.Max;
current_checkbox.id = id;
current_checkbox.label_name = label;
current_checkbox.value = v;
}
// Проверк, если нажали на любую другую позицию вместе нашего окна
if (g.IO.MouseClicked[0] && !hovered && open_menu)
{
auto cursor_pos = ImGui::GetIO().MousePos;
if(cursor_pos.x > menu_pos.x + HOTKEY_MENU_SIZE.x || cursor_pos.x < menu_pos.x
|| cursor_pos.y < menu_pos.y || cursor_pos.y > menu_pos.y + HOTKEY_MENU_SIZE.y)
open_menu = false;
}
const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
ImGui::RenderNavHighlight(total_bb, id);
ImGui::RenderFrame(check_bb.Min + ImVec2(0, 4), check_bb.Max + ImVec2(0, 4), ImGui::GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
if (held || hovered)
{
window->DrawList->AddRect(check_bb.Min + ImVec2(-1, 3), check_bb.Max + ImVec2(1, 5), ImColor(0, 0, 0));
}
ImU32 check_col = ImGui::GetColorU32(ImGuiCol_CheckMark);
if (*v)
{
const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
ImGui::RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad) + ImVec2(0, 4), check_col, square_sz - pad * 2.0f);
}
ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
if (label_size.x > 0.0f)
ImGui::RenderText(label_pos, label);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
return pressed;
}
C++:
// Просто дефолтная функция бинда из CSGOSimple
bool ImGui_HotKeys::Hotkey(const char* label, int& k, const ImVec2& size_arg)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
ImVec2 size = ImGui::CalcItemSize(size_arg, ImGui::CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
const ImRect frame_bb(window->DC.CursorPos + ImVec2(label_size.x + style.ItemInnerSpacing.x, 0.0f), window->DC.CursorPos + size + ImVec2(ImGui::CalcTextSize("<Press a key>", NULL, true).x / 2, 0));
const ImRect total_bb(window->DC.CursorPos, frame_bb.Max);
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, id))
return false;
const bool hovered = ImGui::ItemHoverable(frame_bb, id);
if (hovered) {
ImGui::SetHoveredID(id);
//g.MouseCursor = ImGuiMouseCursor_TextInput;
}
const bool user_clicked = hovered && io.MouseClicked[0];
if (user_clicked) {
if (g.ActiveId != id) {
// Start edition
memset(io.MouseDown, 0, sizeof(io.MouseDown));
memset(io.KeysDown, 0, sizeof(io.KeysDown));
k = 0;
}
ImGui::SetActiveID(id, window);
ImGui::FocusWindow(window);
}
else if (io.MouseClicked[0]) {
// Release focus when we click outside
if (g.ActiveId == id)
ImGui::ClearActiveID();
}
bool value_changed = false;
int key = k;
if (g.ActiveId == id) {
for (auto i = 0; i < 5; i++) {
if (io.MouseDown[i]) {
switch (i) {
case 0:
key = VK_LBUTTON;
break;
case 1:
key = VK_RBUTTON;
break;
case 2:
key = VK_MBUTTON;
break;
case 3:
key = VK_XBUTTON1;
break;
case 4:
key = VK_XBUTTON2;
break;
}
value_changed = true;
ImGui::ClearActiveID();
}
}
if (!value_changed) {
for (auto i = VK_BACK; i <= VK_RMENU; i++) {
if (io.KeysDown[i]) {
key = i;
value_changed = true;
ImGui::ClearActiveID();
}
}
}
if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) {
k = 0;
ImGui::ClearActiveID();
}
else {
k = key;
}
}
// Render
// Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is Set 'buf' might still be the old value. We Set buf to NULL to prevent accidental usage from now on.
char buf_display[64] = "...";
ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImVec4(0.20f, 0.25f, 0.30f, 1.0f)), true, style.FrameRounding);
if (k != 0 && g.ActiveId != id) {
strcpy_s(buf_display, KeyNames[k]);
}
if (g.ActiveId == id) {
strcpy_s(buf_display, "<Press a key>");
}
const ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
ImVec2 render_pos = frame_bb.Min + style.FramePadding;
ImGui::RenderTextClipped(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding, buf_display, NULL, NULL, style.ButtonTextAlign, &clip_rect);
if (label_size.x > 0)
ImGui::RenderText(ImVec2(total_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), label);
return value_changed;
}
C++:
// А вот и самое вкусное что вам нужно будет. Имеется часть говнокода, но я её не мог избежать по тем или иным причинам
bool ImGui_HotKeys::Combo_CheckBox(const char* label, int& current_item, std::vector<ImGui_HotKey_Checkbox> &data)
{
if (data.size() == 0)
data.push_back(ImGui_HotKey_Checkbox::ImGui_HotKey_Checkbox(current_checkbox.id, current_checkbox.value));
// Проверка, существует ли кейбинды для нашего checkbox'а, если нет, то создаём и ставим current_item на
// последний добавленный элемент
if (std::all_of(data.begin(), data.end(), [](ImGui_HotKey_Checkbox h) {return h.id != current_checkbox.id; }))
{
data.push_back(ImGui_HotKey_Checkbox::ImGui_HotKey_Checkbox(current_checkbox.id, current_checkbox.value));
current_item = data.size() - 1;
}
// Если current_item не равен активному checbox'у то надо найти его первое вхождение и выставить наш current_item
if (data[current_item].id != current_checkbox.id)
{
for (int i = 0; i < data.size(); i++)
{
if (data[i].id == current_checkbox.id)
{
current_item = i;
break;
}
}
}
// Main code
ImGuiContext& g = *GImGui;
if (data[current_item].key != 0)
{
data[current_item].name = "Bind ";
data[current_item].name += KeyNames[data[current_item].key];
}
else {
data[current_item].name = "Unknown bind";
}
std::string preview_name = data[current_item].name;
int items_count = data.size();
if (!ImGui::BeginCombo("##__keybind.combo__", preview_name.c_str(), ImGuiComboFlags_HeightRegular | ImGuiComboFlags_NoArrowButton))
return false;
// Display items
bool value_changed = false;
int delete_index = -1;
bool found_hotkey = false;
for (int i = 0; i < items_count; i++)
{
if (data[i].id != current_checkbox.id)
continue;
ImGui::PushID((void*)(intptr_t)i);
const bool item_selected = (i == current_item);
if (data[i].key != 0)
{
data[i].name = "Bind ";
data[i].name += KeyNames[data[i].key];
}
else {
data[i].name = "Empty bind";
}
if (ImGui::Selectable(data[i].name.c_str(), item_selected, 0, ImVec2(80, 20)))
{
value_changed = true;
current_item = i;
}
if (items_count > 1)
{
ImGui::SameLine();
// Здесь имеется небольшой баг, увидети его, если будуте использовать данный код для своих нужд
if (ImGui::Selectable("-", item_selected, ImGuiSelectableFlags_DontClosePopups, ImVec2(20, 20)))
{
// Так как current item останется на месте при удалении последнего элемента, мы можем указывать
// на элемент массива, которого не существует и получим exception out of range
if (data.begin() + i <= data.end())
{
if (i < current_item)
current_item--;
if (i > current_item)
current_item++;
if (i == current_item)
current_item = 0;
data.erase(data.begin() + i);
ImGui::PopID();
break;
}
}
}
if (i == items_count - 1)
{
ImGui::SetCursorPosX((ImGui::GetCurrentWindow()->Size.x / 2) - ImGui::CalcTextSize("add_one").x / 2);
if (ImGui::Button("Add Bind ", ImVec2(0, 0)))
{
data.push_back(ImGui_HotKey_Checkbox::ImGui_HotKey_Checkbox(current_checkbox.id, current_checkbox.value));
}
}
if (item_selected)
ImGui::SetItemDefaultFocus();
ImGui::PopID();
}
ImGui::EndCombo();
return value_changed;
}
C++:
// Вот и заключительная функция для отрисовки,
void ImGui_HotKeys::Draw()
{
// Просто защита от дебила
if (!Menu::Get().IsVisible() || !open_menu)
return;
static int key = 0;
static int current_key = 0;
ImGui::SetNextWindowSize(HOTKEY_MENU_SIZE);
ImGui::SetNextWindowPos(menu_pos);
ImGui::Begin("Keybind", 0, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoNav);
{
//ImGui::Checkbox("biba", &b);
ImGui::Text("HotKey list"); ImGui::SameLine();
Combo_CheckBox("##list.of.keybinds", current_key, checkbox_hotkeys);
ImGui::Text("Mode"); ImGui::SameLine();
ImGui::Combo("##keybind.mode", &checkbox_hotkeys[current_key].mode, key_modes, IM_ARRAYSIZE(key_modes));
Hotkey("Key", checkbox_hotkeys[current_key].key);
}
ImGui::End();
}
Вот в общем-то основной код, который вам понадобится. Но надо бы написать ещё функцию, которая будет проверять, нажата ли кнопка и включать или выключать наш Checkbox. Вот простенький код для этих двух режимов
C++:
void CHotKeysSystem::Run()
{
// Checkbox checking
auto checkbox_hotkeys = ImGui_HotKeys::checkbox_hotkeys;
for (auto &hk : ImGui_HotKeys::checkbox_hotkeys)
{
if (hk.mode == MODE_HOLD)
{
*hk.value = InputSys::Get().IsKeyDown(hk.key);
}
if (hk.mode == MODE_TOGGLE)
{
if (InputSys::Get().WasKeyPressed(hk.key))
*hk.value = !(*hk.value);
}
}
}
Как-то так мои дорогие друзьяшки. Думаю не буду ставить даже хайд на данную тему, я просто не хочу, чтобы люди использовали кейбинд систему как в лв или прямо в функциях проверяли активна ли кнопка для данного чекбокса. Так что готов к любой критике, надеюсь мой гайд хоть немножко помог пользователям данного форума)
C++:
static const char* const KeyNames[] = {
"Unknown",
"MOUSE1",
"MOUSE2",
"CANCEL",
"MOUSE3",
"MOUSE4",
"MOUSE5",
"Unknown",
"BACKSPACE",
"TAB",
"Unknown",
"Unknown",
"CLEAR",
"ENTER",
"Unknown",
"Unknown",
"SHIFT",
"CTRL",
"ALT",
"PAUSE BREAK",
"CAPS LOCK",
"KANA",
"Unknown",
"JUNJA",
"FINAL",
"KANJI",
"Unknown",
"ESC",
"CON",
"VK_NONCONVERT",
"ACCEPT",
"M CHANGE",
"SPACE",
"PGUP",
"PGDOWN",
"END",
"HOME",
"LEFT",
"UP",
"RIGHT",
"DOWN",
"SELECT",
"PRINT",
"EXECUTE",
"SNAP",
"INSIRT",
"DELETE",
"HELP",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"LWIN",
"RWIN",
"APPS",
"Unknown",
"SLEEP",
"N0",
"N1",
"N2",
"N3",
"N4",
"N5",
"N6",
"N7",
"N8",
"N9",
"MULTIPLY",
"ADD",
"SEPARATOR",
"SUBTRACT",
"DECIMAL",
"DIVIDE",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"F13",
"F14",
"F15",
"F16",
"F17",
"F18",
"F19",
"F20",
"F21",
"F22",
"F23",
"F24",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"NUMLOCK",
"SCROLL LOCK",
"OEM_NEC_EQUAL",
"OEM_FJ_MASSHOU",
"OEM_FJ_TOUROKU",
"OEM_FJ_LOYA",
"OEM_FJ_ROYA",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"LSHIFT",
"RSHIFT",
"LCTRL",
"RCTRL",
"LMENU",
"RMENU"
};
Так же вот вам и SS: