-
Автор темы
- #1
Credits to Silv for this
video of the colorpicker:
video of the colorpicker:
Пожалуйста, авторизуйтесь для просмотра ссылки.
colorpicker.cpp:
#include "includes.h"
// static variables.
int Colorpicker::texture = 0;;
std::unique_ptr< Color[] > Colorpicker::gradient = nullptr;;
constexpr int PICKER_SIZE = 200;
constexpr int HUE_BAR_WIDTH = 20;
constexpr int ALPHA_BAR_HEIGHT = 15;
constexpr int PICKER_PADDING = 5;
void rgb_to_hsv(int r, int g, int b, float& h, float& s, float& v) {
float r_norm = r / 255.0f;
float g_norm = g / 255.0f;
float b_norm = b / 255.0f;
float cmax = std::max({ r_norm, g_norm, b_norm });
float cmin = std::min({ r_norm, g_norm, b_norm });
float diff = cmax - cmin;
v = cmax;
if (cmax == 0.0f) {
s = 0.0f;
}
else {
s = diff / cmax;
}
if (diff == 0.0f) {
h = 0.0f;
}
else if (cmax == r_norm) {
h = fmod((60 * ((g_norm - b_norm) / diff) + 360), 360) / 360.0f;
}
else if (cmax == g_norm) {
h = fmod((60 * ((b_norm - r_norm) / diff) + 120), 360) / 360.0f;
}
else {
h = fmod((60 * ((r_norm - g_norm) / diff) + 240), 360) / 360.0f;
}
}
void Colorpicker::init() {
const int w{ COLORPICKER_PICKER_SIZE };
const int h{ COLORPICKER_PICKER_SIZE };
// should never happen.
if (gradient)
return;
// allocate.
gradient = std::make_unique< Color[] >(w * h);
// init.
float hue{}, sat{ 0.99f }, lum{ 1.f };
// iterate width.
for (int i{}; i < w; ++i) {
// iterate height.
for (int j{}; j < h; ++j) {
// write back to array.
[I](Color[/I])(gradient.get() + i + j * w) = Color::hsl_to_rgb(hue, sat, lum);
hue += (1.f / w);
}
lum -= (1.f / h);
hue = 0.f;
}
// allocate new texture in engine.
texture = g_csgo.m_surface->CreateNewTextureID(true);
// assign allocated memory containing the picker to said texture.
g_csgo.m_surface->DrawSetTextureRGBA(texture, gradient.get(), w, h);
}
void Colorpicker::draw() {
Rect area{ ((Form*)m_parent)->GetElementsRect() };
Point p{ area.x + m_pos.x, area.y + m_pos.y };
render::menu_shade.string(px + LABEL_OFFSET, py - 2, { 205, 205, 205, ((Form*)m_parent)->m_alpha }, m_label);
render::rect(px + m_w - COLORPICKER_WIDTH, py, COLORPICKER_WIDTH, COLORPICKER_HEIGHT, { 0, 0, 0, ((Form*)m_parent)->m_alpha });
render::rect_filled(px + m_w - COLORPICKER_WIDTH + 1, py + 1, COLORPICKER_WIDTH - 2, COLORPICKER_HEIGHT - 2, m_color);
render::rect_filled_fade(px + m_w - COLORPICKER_WIDTH + 1, py + 1, COLORPICKER_WIDTH - 2, COLORPICKER_HEIGHT - 2, { 255, 255, 255, 20 }, 0, 150);
if (m_open) {
render::rect_filled(px, py + COLORPICKER_HEIGHT + PICKER_PADDING, PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING, PICKER_SIZE + ALPHA_BAR_HEIGHT + PICKER_PADDING * 2, { 30, 30, 30, 255 });
render::rect(px, py + COLORPICKER_HEIGHT + PICKER_PADDING, PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING, PICKER_SIZE + ALPHA_BAR_HEIGHT + PICKER_PADDING * 2, { 0, 0, 0, 255 });
if (m_gradient_texture == 0 || m_last_hue != m_hue) {
update_gradient_texture();
}
g_csgo.m_surface->DrawSetColor(Color(255, 255, 255, 255));
g_csgo.m_surface->DrawSetTexture(m_gradient_texture);
g_csgo.m_surface->DrawTexturedRect(px + PICKER_PADDING, py + COLORPICKER_HEIGHT + PICKER_PADDING * 2, px + PICKER_SIZE + PICKER_PADDING, py + COLORPICKER_HEIGHT + PICKER_SIZE + PICKER_PADDING * 2);
for (int i = 0; i < PICKER_SIZE; ++i) {
float hue = i / float(PICKER_SIZE);
render::rect_filled(px + PICKER_SIZE + PICKER_PADDING * 2, py + COLORPICKER_HEIGHT + PICKER_PADDING * 2 + i, HUE_BAR_WIDTH, 1, Color::hsv_to_rgb(hue, 1.0f, 1.0f));
}
int alpha_bar_y = py + COLORPICKER_HEIGHT + PICKER_SIZE + PICKER_PADDING * 3;
draw_checkered_background(px + PICKER_PADDING, alpha_bar_y, PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING, ALPHA_BAR_HEIGHT);
render::rect_filled_fade(px + PICKER_PADDING, alpha_bar_y, PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING, ALPHA_BAR_HEIGHT,
Color(m_color.r(), m_color.g(), m_color.b(), 255), Color(m_color.r(), m_color.g(), m_color.b(), 0), true);
draw_crosshair(px + PICKER_PADDING + m_saturation * PICKER_SIZE, py + COLORPICKER_HEIGHT + PICKER_PADDING * 2 + (1 - m_value) * PICKER_SIZE, { 255, 255, 255, 255 });
int hue_y = py + COLORPICKER_HEIGHT + PICKER_PADDING * 2 + m_hue * PICKER_SIZE;
render::rect(px + PICKER_SIZE + PICKER_PADDING * 2, hue_y - 1, HUE_BAR_WIDTH, 3, { 255, 255, 255, 255 });
int alpha_x = px + PICKER_PADDING + m_alpha * (PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING);
render::rect(alpha_x - 1, alpha_bar_y, 3, ALPHA_BAR_HEIGHT, { 255, 255, 255, 255 });
}
}
void Colorpicker::draw_checkered_background(int x, int y, int w, int h) {
constexpr int check_size = 5;
for (int i = 0; i < w; i += check_size) {
for (int j = 0; j < h; j += check_size) {
Color color = ((i / check_size + j / check_size) % 2 == 0) ? Color(200, 200, 200, 255) : Color(100, 100, 100, 255);
render::rect_filled(x + i, y + j, check_size, check_size, color);
}
}
}
void Colorpicker::draw_crosshair(int x, int y, Color color) {
constexpr int size = 5;
render::line(x - size, y, x + size, y, color);
render::line(x, y - size, x, y + size, color);
}
void Colorpicker::update_gradient_texture() {
if (m_gradient_texture == 0) {
m_gradient_texture = g_csgo.m_surface->CreateNewTextureID(true);
}
std::vector<Color> gradient_data(PICKER_SIZE * PICKER_SIZE);
for (int y = 0; y < PICKER_SIZE; ++y) {
for (int x = 0; x < PICKER_SIZE; ++x) {
float saturation = x / float(PICKER_SIZE - 1);
float value = 1.0f - (y / float(PICKER_SIZE - 1));
gradient_data[y * PICKER_SIZE + x] = Color::hsv_to_rgb(m_hue, saturation, value);
}
}
g_csgo.m_surface->DrawSetTextureRGBA(m_gradient_texture, reinterpret_cast<unsigned char*>(gradient_data.data()), PICKER_SIZE, PICKER_SIZE);
m_last_hue = m_hue;
}
void Colorpicker::think() {
Rect area{ ((Form*)m_parent)->GetElementsRect() };
Point p{ area.x + m_pos.x, area.y + m_pos.y };
Rect preview{ px + m_w - COLORPICKER_WIDTH, py, COLORPICKER_WIDTH, COLORPICKER_HEIGHT };
if (g_input.IsCursorInRect(preview) && g_input.GetKeyPress(VK_LBUTTON)) {
m_open = !m_open;
if (m_open) {
((Form*)m_parent)->m_active_element = this;
rgb_to_hsv(m_color.r(), m_color.g(), m_color.b(), m_hue, m_saturation, m_value);
m_alpha = m_color.a() / 255.0f;
}
}
if (m_open) {
Rect picker{ px + PICKER_PADDING, py + COLORPICKER_HEIGHT + PICKER_PADDING * 2, PICKER_SIZE, PICKER_SIZE };
Rect hue_bar{ px + PICKER_SIZE + PICKER_PADDING * 2, py + COLORPICKER_HEIGHT + PICKER_PADDING * 2, HUE_BAR_WIDTH, PICKER_SIZE };
Rect alpha_bar{ px + PICKER_PADDING, py + COLORPICKER_HEIGHT + PICKER_SIZE + PICKER_PADDING * 3, PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING, ALPHA_BAR_HEIGHT };
if (g_input.GetKeyState(VK_LBUTTON)) {
if (g_input.IsCursorInRect(picker) || m_dragging_picker) {
m_dragging_picker = true;
m_saturation = std::clamp((g_input.m_mouse.x - picker.x) / float(PICKER_SIZE), 0.0f, 1.0f);
m_value = 1.0f - std::clamp((g_input.m_mouse.y - picker.y) / float(PICKER_SIZE), 0.0f, 1.0f);
}
else if (g_input.IsCursorInRect(hue_bar) || m_dragging_hue) {
m_dragging_hue = true;
m_hue = std::clamp((g_input.m_mouse.y - hue_bar.y) / float(PICKER_SIZE), 0.0f, 1.0f);
}
else if (g_input.IsCursorInRect(alpha_bar) || m_dragging_alpha) {
m_dragging_alpha = true;
m_alpha = std::clamp((g_input.m_mouse.x - alpha_bar.x) / float(PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING), 0.0f, 1.0f);
}
}
else {
m_dragging_picker = m_dragging_hue = m_dragging_alpha = false;
}
m_color = Color::hsv_to_rgb(m_hue, m_saturation, m_value);
m_color.a() = m_alpha * 255;
if (g_input.GetKeyPress(VK_LBUTTON) && !g_input.IsCursorInRect(Rect{ px, py, PICKER_SIZE + HUE_BAR_WIDTH + PICKER_PADDING * 3, COLORPICKER_HEIGHT + PICKER_SIZE + ALPHA_BAR_HEIGHT + PICKER_PADDING * 4 } )) {
m_open = false;
if (m_parent) {
Form* parentForm = static_cast<Form*>(m_parent);
if (parentForm && parentForm->m_active_element == this) {
parentForm->m_active_element = nullptr;
}
}
if (m_callback) {
m_callback();
}
}
}
if (m_ptr)
*m_ptr = m_color;
}
void Colorpicker::click() {
}
void Colorpicker::toggle_open() {
m_open = !m_open;
if (m_open)
AddFlags(ElementFlags::EXPANDED);
else
RemoveFlags(ElementFlags::EXPANDED);
}
colorpicker.h:
#pragma once
#define COLORPICKER_WIDTH 16
#define COLORPICKER_HEIGHT 8
#define COLORPICKER_PICKER_SIZE 256
//class TextBox;
class Colorpicker : public Element {
public:
__forceinline Colorpicker() : m_open{ false }, m_label{}, m_color{}, m_ptr{ nullptr } {
m_flags = ElementFlags::DRAW | ElementFlags::CLICK | ElementFlags::ACTIVE | ElementFlags::SAVE | ElementFlags::DEACIVATE;
m_type = ElementTypes::COLORPICKER;
m_h = m_base_h = COLORPICKER_HEIGHT;
m_use_label = true;
m_show = true;
}
__forceinline void setup(const std::string& label, const std::string& file_id, Color color, Color* ptr = nullptr) {
m_label = label;
m_file_id = file_id;
m_color = color;
m_alpha = m_color.a() / 255.0f;
m_ptr = ptr;
if (m_ptr)
*m_ptr = m_color;
}
__forceinline void set(Color color) {
bool changed = m_color.rgba() != color.rgba();
m_color = color;
m_alpha = m_color.a() / 255.0f;
if (m_ptr)
*m_ptr = m_color;
if (changed && m_callback)
m_callback();
}
__forceinline void set_alpha(float alpha) {
m_alpha = std::clamp(alpha, 0.0f, 1.0f);
m_color.a() = static_cast<int>(m_alpha * 255.0f);
if (m_ptr)
m_ptr->a() = m_color.a();
if(m_callback)
m_callback();
}
bool is_expanded() const override {
return m_open;
}
__forceinlineColor get() {
return m_color;
}
__forceinline float get_alpha() {
return m_alpha;
}
static void init();
static __forceinline Color ColorFromPos(int x, int y) {
return [I](Color[/I])(gradient.get() + x + y * COLORPICKER_PICKER_SIZE);
}
public:
static int texture;
static std::unique_ptr< Color[] > gradient;
bool m_right_click_open;
//TextBox* m_rgb_textbox;
static const int PICKER_SIZE = 200;
static const int HUE_BAR_WIDTH = 20;
static const int ALPHA_BAR_HEIGHT = 20;
float m_hue = 0.0f;
float m_saturation = 1.0f;
float m_value = 1.0f;
float m_alpha = 1.0f;
int m_gradient_texture = 0;
float m_last_hue = -1.0f;
bool m_dragging_picker = false;
bool m_dragging_hue = false;
bool m_dragging_alpha = false;
//void open();
//void close();
protected:
bool m_open;
std::string m_label;
Color m_color;
Color* m_ptr;
protected:
void draw() override;
void draw_checkered_background(int x, int y, int w, int h);
void draw_crosshair(int x, int y, Color color);
//void draw_arrow(int x, int y, bool vertical, Color color);
void update_gradient_texture();
void think() override;
void click() override;
void toggle_open();
//void create_rgb_textbox();
//void update_rgb_textbox();
};
color.h:
static Color hsv_to_rgb(float h, float s, float v) {
float r, g, b;
if (s == 0.0f) {
r = g = b = v;
}
else {
int i = int(h * 6.0f);
float f = (h * 6.0f) - i;
float p = v * (1.0f - s);
float q = v * (1.0f - s * f);
float t = v * (1.0f - s * (1.0f - f));
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
}
return Color(r * 255, g * 255, b * 255);
}
element.h:
add this to elementflags:
EXPANDED = 1 << 7
add in element class:
int m_z_index; (you should initialize this variable in the element constructor)
virtual bool is_expanded() const {
return false;
}
form.cpp:
// !! replace this with the previous displaying logic after the "// iterate elements to display." comment!!!
// sort elements by z-index.
std::sort(m_active_tab->m_elements.begin(), m_active_tab->m_elements.end(),
[](const Element* a, const Element* b) {
return a->m_z_index < b->m_z_index;
});
// draw non-expanded elements.
for (const auto& e : m_active_tab->m_elements) {
if (!e->m_show || !(e->m_flags & ElementFlags::DRAW) || e->is_expanded())
continue;
e->draw();
}
// draw expanded elements.
for (const auto& e : m_active_tab->m_elements) {
if (!e->m_show || !(e->m_flags & ElementFlags::DRAW) || !e->is_expanded())
continue;
e->draw();
}
// draw active element last if it's different from expanded elements.
if (m_active_element && m_active_element->m_show && !m_active_element->is_expanded()) {
m_active_element->draw();
}
}
}
}
gui.cpp (important):
void GUI::think() {
m_open = false;
// update keyboard input.
g_input.update();
// process keybinds.
for (const auto& f : m_forms) {
if (!f) continue;
for (const auto& t : f->m_tabs) {
if (!t) continue;
for (const auto& e : t->m_elements) {
if (!e || e->m_type != ElementTypes::KEYBIND) continue;
Keybind* k = static_cast<Keybind*>(e);
if (k->m_toggle && g_input.GetKeyPress(k->get())) {
k->m_toggle();
goto forms_sort;
}
}
}
}
forms_sort:
// sort forms based on last touched tick.
std::sort(m_forms.begin(), m_forms.end(), [](Form* a, Form* b) {
return a->m_tick > b->m_tick;
});
// update form open states.
for (const auto& f : m_forms) {
if (!f) continue;
if (f->m_key != -1 && g_input.GetKeyPress(f->m_key)) f->toggle();
if (!m_open && f->m_open) m_open = true;
if (!f->m_open && f->m_active_element) f->m_active_element = nullptr;
}
// update element visibility.
for (const auto& f : m_forms) {
if (!f || !f->m_active_tab || f->m_active_tab->m_elements.empty()) continue;
f->m_active_tab->m_element_offset.fill(ELEMENT_BORDER);
for (auto& e : f->m_active_tab->m_elements) {
e->m_show = true;
for (auto& cb : e->m_show_callbacks) {
if (!cb()) {
e->m_show = false;
break;
}
}
if (e->m_show) {
if (!e->m_use_label)
f->m_active_tab->m_element_offset[e->m_col] -= (ELEMENT_DISTANCE - ELEMENT_COMBINED_DISTANCE);
e->m_pos.y = f->m_active_tab->m_element_offset[e->m_col];
f->m_active_tab->m_element_offset[e->m_col] += (ELEMENT_COMBINED_DISTANCE + e->m_base_h);
}
}
}
// handle mouse input and dragging.
bool pressed = g_input.GetKeyPress(VK_LBUTTON);
bool down = g_input.GetKeyState(VK_LBUTTON);
g_csgo.m_input_system->GetCursorPosition(&g_input.m_mouse.x, &g_input.m_mouse.y);
math::clamp(g_input.m_mouse.x, 0, g_cl.m_width);
math::clamp(g_input.m_mouse.y, 0, g_cl.m_height);
if (m_drag_form && !down) m_drag_form = nullptr;
if (m_drag_form && down && !pressed) {
m_drag_form->m_x = g_input.m_mouse.x - m_drag_offset.x;
m_drag_form->m_y = g_input.m_mouse.y - m_drag_offset.y;
}
if (pressed || down) {
for (const auto& f : m_forms) {
if (!f) continue;
if (g_input.IsCursorInRect(f->GetFormRect())) {
f->m_tick = g_winapi.GetTickCount();
break;
}
}
}
std::sort(m_forms.begin(), m_forms.end(), [](Form* a, Form* b) {
return a->m_tick > b->m_tick;
});
// handle expanded elements.
Element* expanded_element = nullptr;
Form* expanded_form = nullptr;
for (const auto& f : m_forms) {
if (!f || !f->m_open || !f->m_active_tab) continue;
for (const auto& e : f->m_active_tab->m_elements) {
if (e->is_expanded()) {
expanded_element = e;
expanded_form = f;
goto forms_interaction;
}
}
}
forms_interaction:
// interact with forms and elements.
for (const auto& f : m_forms) {
if (!f || !f->m_open) continue;
bool element_input = false;
Rect cl = f->GetClientRect();
Rect el = f->GetElementsRect();
if (pressed) {
if (g_input.IsCursorInRect(f->GetFormRect())) {
if (g_input.IsCursorInRect(cl)) {
Rect tabs_area = f->GetTabsRect();
if (g_input.IsCursorInRect(tabs_area) && !f->m_tabs.empty()) {
if (f->m_active_element) f->m_active_element = nullptr;
for (size_t i = 0; i < f->m_tabs.size(); ++i) {
const auto& t = f->m_tabs[i];
Rect tab{ tabs_area.x, tabs_area.y + static_cast<int>(i) * 16, tabs_area.w, 16 };
if (g_input.IsCursorInRect(tab)) f->m_active_tab = t;
}
}
else if (g_input.IsCursorInRect(el)) {
element_input = true;
}
}
else {
m_drag_form = f;
m_drag_offset.x = g_input.m_mouse.x - f->m_x;
m_drag_offset.y = g_input.m_mouse.y - f->m_y;
if (f->m_active_element) f->m_active_element = nullptr;
}
}
else if (f->m_active_element) {
f->m_active_element = nullptr;
}
}
if (!expanded_element || f == expanded_form) {
if (f->m_active_element && f->m_active_element->m_show) {
f->m_active_element->think();
if (f->m_active_element) {
Rect area{ el.x + f->m_active_element->m_pos.x, el.y + f->m_active_element->m_pos.y, f->m_active_element->m_w, f->m_active_element->m_h };
if (element_input && (f->m_active_element->m_flags & ElementFlags::CLICK) && g_input.IsCursorInRect(area)) {
f->m_active_element->click();
if (f->m_active_element->m_flags & ElementFlags::DEACIVATE)
f->m_active_element = nullptr;
element_input = false;
}
}
}
if (f->m_active_tab && !f->m_active_tab->m_elements.empty()) {
for (const auto& e : f->m_active_tab->m_elements) {
if (!e || (f->m_active_element && e == f->m_active_element))
continue;
if (!e->m_show)
continue;
e->think();
if (expanded_element && e != expanded_element)
continue;
Rect area{ el.x + e->m_pos.x, el.y + e->m_pos.y, e->m_w, e->m_h };
if (element_input && (e->m_flags & ElementFlags::CLICK) && g_input.IsCursorInRect(area)) {
e->click();
element_input = false;
if (e->m_flags & ElementFlags::ACTIVE)
f->m_active_element = e;
else if (f->m_active_element)
f->m_active_element = nullptr;
}
}
}
if (element_input && f->m_active_element)
f->m_active_element = nullptr;
}
}
// do the rendering.
draw();
}
Последнее редактирование: