Начинающий
- Статус
- Оффлайн
- Регистрация
- 15 Окт 2019
- Сообщения
- 45
- Реакции
- 8
render.cpp:
int render::Font::calc_width(const std::string& text) {
// get the width of the string in pixels.
FontSize_t size = this->size(text);
return size.m_width;
}
form.h:
#pragma once
class Form {
friend class GUI;
friend class Element;
friend class Dropdown;
friend class MultiDropdown;
friend class Colorpicker;
friend class Edit;
friend class Config;
public:
bool m_open;
float m_opacity;
int m_alpha;
private:
std::string m_title;
int m_key;
int m_x;
int m_y;
int m_width;
int m_height;
int m_tick;
std::vector<Tab*> m_tabs;
Tab* m_active_tab;
Element* m_active_element;
public:
__forceinline Form() : m_open{ true }, m_opacity{}, m_alpha{}, m_title{}, m_key{ -1 }, m_x{}, m_y{}, m_width{}, m_height{}, m_tick{}, m_tabs{}, m_active_tab{}, m_active_element{} {
}
__forceinline void SetTitle(const std::string& title) {
m_title = title;
}
__forceinline void SetPosition(int x, int y) {
m_x = x;
m_y = y;
}
__forceinline void SetSize(int w, int h) {
m_width = w;
m_height = h;
}
__forceinline void SetToggle(int key) {
m_key = key;
}
// adds a new tab.
__forceinline void RegisterTab(Tab* tab) {
// if this is the first tab, set the active tab.
if (!m_tabs.size())
m_active_tab = tab;
// this form is the owner of the tab.
tab->m_parent = this;
// add the tab to our container.
m_tabs.push_back(tab);
}
// get specific tab based off name.
__forceinline Tab* GetTabByName(const std::string& name) const {
auto it = std::find_if(m_tabs.begin(), m_tabs.end(),
[&name](Tab* tab) {
return !tab->m_title.compare(name);
});
if (it != m_tabs.end())
return *it;
return nullptr;
}
// toggles the form.
__forceinline void toggle() {
m_open = !m_open;
}
// get the click-able area.
__forceinline Rect GetClientRect() const {
return { m_x + 6, m_y + 28, m_width - 12, m_height - 34 };
}
__forceinline Rect GetFormRect() const {
return { m_x, m_y, m_width, m_height };
}
__forceinline Rect GetTabsRect() const {
return { m_x + 20, m_y + 40, 100, m_height - 60 };
}
__forceinline Rect GetElementsRect() const {
Rect tabs_area = GetTabsRect();
return { tabs_area.x + tabs_area.w + 20, m_y + 40, m_width - tabs_area.w - 60, m_height - 60 };
}
private:
__forceinline void update_opacity();
__forceinline void draw_background(int x, int y, int width, int height, Color color);
__forceinline void draw_tabs();
__forceinline void draw_elements();
public:
void draw();
};
form.cpp:
#include "../../../includes.h"
// underline animation state.
struct TabUnderlineAnim {
float x;
float w;
float target_x;
float target_w;
float speed;
bool initialized;
TabUnderlineAnim() : x(0), w(0), target_x(0), target_w(0), speed(12.0f), initialized(false) {}
};
static TabUnderlineAnim g_tab_underline;
void Form::update_opacity() {
// opacity should reach 1 in 500 milliseconds.
constexpr float frequency = 1.f / 0.5f;
// the increment / decrement per frame.
float step = frequency * g_csgo.m_globals->m_frametime;
// if open -> increment
// if closed -> decrement
m_open ? m_opacity += step : m_opacity -= step;
// clamp the opacity.
math::clamp(m_opacity, 0.f, 1.f);
// apply ease-out curve for a smoother animation.
float eased_opacity = 1.f - (1.f - m_opacity) * (1.f - m_opacity);
m_alpha = 0xff * eased_opacity;
}
void Form::draw_background(int x, int y, int width, int height, Color color) {
// draw background.
render::rect_filled(x, y, width, height, color);
// draw a subtle border.
render::rect(x, y, width, height, { 5, 5, 5, m_alpha });
// draw the title text.
render::menu.string(x + width / 2, y + 10, { 205, 205, 205, m_alpha }, m_title, render::ALIGN_CENTER);
}
void Form::draw_tabs() {
// check if we have any tabs.
if (m_tabs.empty())
return;
// get the tabs area.
Rect tabs_area = GetTabsRect();
// draw the background.
draw_background(tabs_area.x, tabs_area.y, tabs_area.w, tabs_area.h, { 17, 17, 17, m_alpha });
Color color = g_gui.m_color;
color.a() = m_alpha;
// tab height and padding.
const int tab_height = 16;
const int tab_padding = 5;
const int tab_text_x = tabs_area.x + 10;
const int tab_text_y = tabs_area.y + tab_padding;
// find active tab index.
int active_index = 0;
for (size_t i = 0; i < m_tabs.size(); ++i) {
if (m_tabs[i] == m_active_tab) {
active_index = (int)i;
break;
}
}
// measure tab widths.
std::vector<int> tab_widths;
tab_widths.reserve(m_tabs.size());
for (const auto& t : m_tabs) {
int w = render::menu.calc_width(t->m_title);
tab_widths.push_back(w);
}
// calculate underline target x and width.
int underline_x = tab_text_x;
for (int i = 0; i < active_index; ++i)
underline_x += tab_widths[i] + 2 * tab_padding;
int underline_w = tab_widths[active_index] + 2 * tab_padding;
// initialize or update underline animation state.
if (!g_tab_underline.initialized || g_tab_underline.target_x != underline_x || g_tab_underline.target_w != underline_w) {
g_tab_underline.target_x = (float)underline_x;
g_tab_underline.target_w = (float)underline_w;
if (!g_tab_underline.initialized) {
g_tab_underline.x = g_tab_underline.target_x;
g_tab_underline.w = g_tab_underline.target_w;
g_tab_underline.initialized = true;
}
}
// interpolate underline position and width.
float lerp_speed = g_tab_underline.speed * g_csgo.m_globals->m_frametime;
g_tab_underline.x += (g_tab_underline.target_x - g_tab_underline.x) * lerp_speed;
g_tab_underline.w += (g_tab_underline.target_w - g_tab_underline.w) * lerp_speed;
// draw tabs vertically, one per row.
int y = tab_text_y;
for (size_t i = 0; i < m_tabs.size(); ++i) {
const auto& t = m_tabs[i];
Color tab_col = (t == m_active_tab) ? color : Color{ 152, 152, 152, m_alpha };
render::menu.string(tab_text_x, y, tab_col, t->m_title);
y += tab_height;
}
// draw animated underline.
Color underline_col = color;
underline_col.a() = m_alpha;
int underline_y = tab_text_y + active_index * tab_height;
render::rect_filled(tab_text_x - 4, underline_y, 2, tab_height, underline_col);
}
void Form::draw_elements() {
// check if we have an active tab.
if (m_active_tab->m_elements.empty())
return;
// elements background and border.
Rect el = GetElementsRect();
// draw the background.
draw_background(el.x, el.y, el.w, el.h, { 17, 17, 17, m_alpha });
// draw the date and username.
std::string date = XOR(__DATE__);
std::string text = tfm::format(XOR("%s | %s"), date.c_str(), g_cl.m_user);
render::menu.string(el.x + el.w - 5, el.y + el.h - 16, { 205, 205, 205, m_alpha }, text, render::ALIGN_RIGHT);
// iterate elements.
for (const auto& e : m_active_tab->m_elements) {
// skip elements that are not to be drawn.
if (!e || (m_active_element && e == m_active_element) || !e->m_show || !(e->m_flags & DRAW))
continue;
// draw the element.
e->draw();
}
// draw the active element last.
if (m_active_element && m_active_element->m_show)
m_active_element->draw();
}
void Form::draw() {
update_opacity();
if (!m_alpha)
return;
draw_background(m_x, m_y, m_width, m_height, { 12, 12, 12, m_alpha });
draw_tabs();
draw_elements();
}