Исходник Supremacy proper colorpicker

Начинающий
Статус
Оффлайн
Регистрация
9 Окт 2024
Сообщения
3
Реакции[?]
2
Поинты[?]
2K
Credits to Silv for this
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();
}
 
Последнее редактирование:
Сверху Снизу