add 'cursor' ui property

This commit is contained in:
MihailRis 2024-12-30 21:41:05 +03:00
parent 10f0bd0290
commit cb9690ebc7
14 changed files with 144 additions and 1 deletions

View File

@ -48,6 +48,7 @@ Properties that apply to all elements:
| tooltip | string | yes | yes | tooltip text |
| tooltipDelay | float | yes | yes | tooltip delay |
| contentOffset | vec2 | yes | *no* | element content offset |
| cursor | string | yes | yes | cursor displayed on hover |
Common element methods:

View File

@ -44,6 +44,7 @@ Examples:
- `gravity` - automatic positioning of the element in the container. (Does not work in automatic containers like panel). Values: *top-left, top-center, top-right, center-left, center-center, center-right, bottom-left, bottom-center, bottom-right*.
- `z-index` - determines the order of elements, with a larger value it will overlap elements with a smaller one.
- `interactive` - if false, hovering over the element and all sub-elements will be ignored.
- `cursor` - the cursor displayed when hovering over the element (arrow/text/pointer/crosshair/ew-resize/ns-resize/...).
# Template attributes

View File

@ -48,6 +48,7 @@ document["worlds-panel"]:clear()
| tooltip | string | да | да | текст всплывающей подсказки |
| tooltipDelay | float | да | да | задержка всплывающей подсказки |
| contentOffset | vec2 | да | *нет* | смещение содержимого |
| cursor | string | да | да | курсор, отображаемый при наведении |
Общие методы элементов:

View File

@ -48,6 +48,7 @@
- `gravity` - автоматическое позиционирование элемента в контейнере. (Не работает в автоматических контейнерах, как panel). Значения: *top-left, top-center, top-right, center-left, center-center, center-right, bottom-left, bottom-center, bottom-right*.
- `z-index` - определяет порядок элементов, при большем значении будет перекрывать элементы с меньшим.
- `interactive` - при значении false наведение на элемент и все под-элементы будет игнорироваться.
- `cursor` - курсор, отображаемый при наведении на элемент (arrow/text/pointer/crosshair/ew-resize/ns-resize/...).
# Атрибуты шаблонов

View File

@ -0,0 +1,39 @@
#include "commons.hpp"
#include <map>
std::optional<CursorShape> CursorShape_from(std::string_view name) {
static std::map<std::string_view, CursorShape> shapes = {
{"arrow", CursorShape::ARROW},
{"text", CursorShape::TEXT},
{"crosshair", CursorShape::CROSSHAIR},
{"pointer", CursorShape::POINTER},
{"ew-resize", CursorShape::EW_RESIZE},
{"ns-resize", CursorShape::NS_RESIZE},
{"nwse-resize", CursorShape::NWSE_RESIZE},
{"nesw-resize", CursorShape::NESW_RESIZE},
{"all-resize", CursorShape::ALL_RESIZE},
{"not-allowed", CursorShape::NOT_ALLOWED}
};
const auto& found = shapes.find(name);
if (found == shapes.end()) {
return std::nullopt;
}
return found->second;
}
std::string to_string(CursorShape shape) {
static std::string names[] = {
"arrow",
"text",
"crosshair",
"pointer",
"ew-resize",
"ns-resize",
"nwse-resize",
"nesw-resize",
"all-resize",
"not-allowed"
};
return names[static_cast<int>(shape)];
}

View File

@ -1,5 +1,8 @@
#pragma once
#include <string>
#include <optional>
enum class DrawPrimitive {
point = 0,
line,
@ -7,9 +10,47 @@ enum class DrawPrimitive {
};
enum class BlendMode {
normal, addition, inversion
/// @brief Normal blending mode.
normal,
/// @brief Additive blending mode.
addition,
/// @brief Subtractive blending mode.
inversion
};
/// @brief Standard GLFW 3.4 cursor shapes (same order and count as in GLFW).
/// It also works in GLFW 3.3 (unsupported shapes will be replaced with the arrow).
enum class CursorShape {
/// @brief Regular arrow
ARROW,
/// @brief Text input I-beam
TEXT,
/// @brief Crosshair
CROSSHAIR,
/// @brief Pointing hand
POINTER,
/// @brief Horizontal resize arrow
EW_RESIZE,
/// @brief Vertical resize arrow
NS_RESIZE,
// GLFW 3.4+ cursor shapes
/// @brief Diagonal resize arrow (top-left to bottom-right)
NWSE_RESIZE,
/// @brief Diagonal resize arrow (top-right to bottom-left)
NESW_RESIZE,
/// @brief All-direction resize arrow
ALL_RESIZE,
/// @brief Operation not allowed
NOT_ALLOWED,
LAST=NOT_ALLOWED
};
std::optional<CursorShape> CursorShape_from(std::string_view name);
std::string to_string(CursorShape shape);
class Flushable {
public:
virtual ~Flushable() = default;

View File

@ -212,6 +212,10 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
batch2D->begin();
container->draw(ctx, assets);
if (hover) {
Window::setCursor(hover->getCursor());
}
}
std::shared_ptr<UINode> GUI::getFocused() const {

View File

@ -25,6 +25,7 @@ TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
input(L""),
placeholder(std::move(placeholder))
{
setCursor(CursorShape::TEXT);
setOnUpPressed(nullptr);
setOnDownPressed(nullptr);
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));

View File

@ -150,6 +150,14 @@ float UINode::getTooltipDelay() const {
return tooltipDelay;
}
void UINode::setCursor(CursorShape shape) {
cursor = shape;
}
CursorShape UINode::getCursor() const {
return cursor;
}
glm::vec2 UINode::calcPos() const {
if (parent) {
return pos + parent->calcPos() + parent->getContentOffset();

View File

@ -1,6 +1,7 @@
#pragma once
#include "delegates.hpp"
#include "graphics/core/commons.hpp"
#include "window/input.hpp"
#include <glm/glm.hpp>
@ -112,6 +113,8 @@ namespace gui {
std::wstring tooltip;
/// @brief element tooltip delay
float tooltipDelay = 0.5f;
/// @brief cursor shape when mouse is over the element
CursorShape cursor = CursorShape::ARROW;
UINode(glm::vec2 size);
public:
@ -206,6 +209,9 @@ namespace gui {
virtual void setTooltipDelay(float delay);
virtual float getTooltipDelay() const;
virtual void setCursor(CursorShape shape);
virtual CursorShape getCursor() const;
virtual glm::vec4 calcColor() const;
/// @brief Get inner content offset. Used for scroll

View File

@ -156,6 +156,11 @@ static void _readUINode(
if (element.has("tooltip-delay")) {
node.setTooltipDelay(element.attr("tooltip-delay").asFloat());
}
if (element.has("cursor")) {
if (auto cursor = CursorShape_from(element.attr("cursor").getText())) {
node.setCursor(*cursor);
}
}
if (auto onclick = create_action(reader, element, "onclick")) {
node.listenAction(onclick);

View File

@ -401,6 +401,10 @@ static int p_get_line_pos(UINode*, lua::State* L) {
return lua::pushcfunction(L, l_get_line_pos);
}
static int p_get_cursor(UINode* node, lua::State* L) {
return lua::pushstring(L, to_string(node->getCursor()));
}
static int l_gui_getattr(lua::State* L) {
auto docname = lua::require_string(L, 1);
auto element = lua::require_string(L, 2);
@ -455,6 +459,7 @@ static int l_gui_getattr(lua::State* L) {
{"paste", p_get_paste},
{"inventory", p_get_inventory},
{"focused", p_get_focused},
{"cursor", p_get_cursor},
};
auto func = getters.find(attr);
if (func != getters.end()) {
@ -616,6 +621,12 @@ static void p_set_focused(
}
}
static void p_set_cursor(UINode* node, lua::State* L, int idx) {
if (auto cursor = CursorShape_from(lua::require_string(L, idx))) {
node->setCursor(*cursor);
}
}
static int l_gui_setattr(lua::State* L) {
auto docname = lua::require_string(L, 1);
auto element = lua::require_string(L, 2);
@ -658,6 +669,7 @@ static int l_gui_setattr(lua::State* L) {
{"checked", p_set_checked},
{"page", p_set_page},
{"inventory", p_set_inventory},
{"cursor", p_set_cursor},
};
auto func = setters.find(attr);
if (func != setters.end()) {

View File

@ -29,6 +29,7 @@ int Window::posY = 0;
int Window::framerate = -1;
double Window::prevSwap = 0.0;
bool Window::fullscreen = false;
CursorShape Window::cursor = CursorShape::ARROW;
static util::ObjectsKeeper observers_keeper;
@ -125,6 +126,8 @@ void error_callback(int error, const char* description) {
}
}
static GLFWcursor* standard_cursors[static_cast<int>(CursorShape::LAST) + 1] = {};
int Window::initialize(DisplaySettings* settings) {
Window::settings = settings;
Window::width = settings->width.get();
@ -219,6 +222,10 @@ int Window::initialize(DisplaySettings* settings) {
logger.info() << "monitor content scale: " << scale.x << "x" << scale.y;
input_util::initialize();
for (int i = 0; i <= static_cast<int>(CursorShape::LAST); i++) {
standard_cursors[i] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR + i);
}
return 0;
}
@ -305,6 +312,9 @@ void Window::popScissor() {
void Window::terminate() {
observers_keeper = util::ObjectsKeeper();
for (int i = 0; i <= static_cast<int>(CursorShape::LAST); i++) {
glfwDestroyCursor(standard_cursors[i]);
}
glfwTerminate();
}
@ -380,6 +390,15 @@ DisplaySettings* Window::getSettings() {
return settings;
}
void Window::setCursor(CursorShape shape) {
if (cursor == shape) {
return;
}
cursor = shape;
// NULL cursor is valid for GLFW
glfwSetCursor(window, standard_cursors[static_cast<int>(shape)]);
}
std::unique_ptr<ImageData> Window::takeScreenshot() {
auto data = std::make_unique<ubyte[]>(width * height * 3);
glPixelStorei(GL_PACK_ALIGNMENT, 1);

View File

@ -5,6 +5,7 @@
#include <stack>
#include <vector>
#include "graphics/core/commons.hpp"
#include "typedefs.hpp"
class ImageData;
@ -20,6 +21,7 @@ class Window {
static bool fullscreen;
static int framerate;
static double prevSwap;
static CursorShape cursor;
static bool tryToMaximize(GLFWwindow* window, GLFWmonitor* monitor);
public:
@ -46,6 +48,8 @@ public:
static void popScissor();
static void resetScissor();
static void setCursor(CursorShape shape);
static void clear();
static void clearDepth();
static void setBgColor(glm::vec3 color);