mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 18:41:51 +00:00
Fractional GUI scale (0.5 step), fit menu to window, fix settings pages
- display.gui-scale is now a number: 0 (auto) or 0.5..4 in 0.5 steps - explicit scale is no longer limited by window size (tiled windows); only an open menu page is shrunk to fit the window - snap Batch2D vertices to pixels on fractional scale (fixes dots above text) - pass app to settings_common via options (not visible inside modules) - gui.get_scale() returns a number
This commit is contained in:
parent
33e132a231
commit
48d5d54b77
13 changed files with 76 additions and 41 deletions
|
|
@ -16,11 +16,11 @@ gui.str(text: str, context: str) -> str
|
||||||
-- Returns size of the main container (window).
|
-- Returns size of the main container (window).
|
||||||
gui.get_viewport() -> {int, int}
|
gui.get_viewport() -> {int, int}
|
||||||
|
|
||||||
-- Returns current GUI scale: window pixels per one UI unit (1..4).
|
-- Returns current GUI scale: window pixels per one UI unit (0.5..4).
|
||||||
-- Set by the display.gui-scale setting and limited by window size.
|
-- Set by the display.gui-scale setting (auto mode fits the window size).
|
||||||
gui.get_scale() -> int
|
gui.get_scale() -> number
|
||||||
|
|
||||||
-- Returns maximal GUI scale that fits the current window size.
|
-- Returns GUI scale used in auto mode (fits the current window size).
|
||||||
gui.get_max_scale() -> int
|
gui.get_max_scale() -> int
|
||||||
|
|
||||||
-- Returns cursor position in UI units (unlike input.get_mouse_pos,
|
-- Returns cursor position in UI units (unlike input.get_mouse_pos,
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,11 @@ gui.str(text: string, context: string) -> string
|
||||||
-- Возвращает размер главного контейнера (окна).
|
-- Возвращает размер главного контейнера (окна).
|
||||||
gui.get_viewport() -> {number, number}
|
gui.get_viewport() -> {number, number}
|
||||||
|
|
||||||
-- Возвращает текущий масштаб интерфейса: пикселей окна в одной единице UI (1..4).
|
-- Возвращает текущий масштаб интерфейса: пикселей окна в одной единице UI (0.5..4).
|
||||||
-- Задаётся настройкой display.gui-scale и ограничивается размером окна.
|
-- Задаётся настройкой display.gui-scale (в режиме Авто подбирается под размер окна).
|
||||||
gui.get_scale() -> int
|
gui.get_scale() -> number
|
||||||
|
|
||||||
-- Возвращает максимальный масштаб интерфейса, помещающийся в текущее окно.
|
-- Возвращает масштаб интерфейса, который выбирается в режиме Авто (помещается в текущее окно).
|
||||||
gui.get_max_scale() -> int
|
gui.get_max_scale() -> int
|
||||||
|
|
||||||
-- Возвращает позицию курсора в единицах UI (в отличие от input.get_mouse_pos,
|
-- Возвращает позицию курсора в единицах UI (в отличие от input.get_mouse_pos,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local settings = require "core:settings_common".new(document, {})
|
local settings = require "core:settings_common".new(document, {app=app})
|
||||||
-- templates call these functions by name, so they must be global
|
-- templates call these functions by name, so they must be global
|
||||||
create_trackbar_setting = settings.create_trackbar_setting
|
create_trackbar_setting = settings.create_trackbar_setting
|
||||||
update_trackbar_label = settings.update_trackbar_label
|
update_trackbar_label = settings.update_trackbar_label
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,18 @@ tostring_overrides["display.framerate"] = function(x)
|
||||||
end
|
end
|
||||||
|
|
||||||
tostring_overrides["display.gui-scale"] = function(x)
|
tostring_overrides["display.gui-scale"] = function(x)
|
||||||
-- scale is limited by the window size, show what is really applied
|
|
||||||
local effective = gui.get_max_scale()
|
|
||||||
if x > 0 then
|
|
||||||
effective = math.min(x, effective)
|
|
||||||
end
|
|
||||||
if x == 0 then
|
if x == 0 then
|
||||||
return string.format("%s (%d)", gui.str("Auto"), effective)
|
return string.format("%s (%d)", gui.str("Auto"), gui.get_max_scale())
|
||||||
elseif effective < x then
|
end
|
||||||
return string.format("%d (%s %d)", x, gui.str("Effective"), effective)
|
local effective = gui.get_scale()
|
||||||
|
if effective ~= x then
|
||||||
|
-- menu is limited by window size
|
||||||
|
return string.format("%s (%s %s)", tostring(x), gui.str("Effective"), tostring(effective))
|
||||||
end
|
end
|
||||||
return tostring(x)
|
return tostring(x)
|
||||||
end
|
end
|
||||||
|
|
||||||
local settings = require "core:settings_common".new(document, {tostring_overrides=tostring_overrides})
|
local settings = require "core:settings_common".new(document, {app=app, tostring_overrides=tostring_overrides})
|
||||||
-- templates call these functions by name, so they must be global
|
-- templates call these functions by name, so they must be global
|
||||||
create_trackbar_setting = settings.create_trackbar_setting
|
create_trackbar_setting = settings.create_trackbar_setting
|
||||||
update_trackbar_label = settings.update_trackbar_label
|
update_trackbar_label = settings.update_trackbar_label
|
||||||
|
|
@ -32,7 +30,7 @@ create_checkbox = settings.create_checkbox
|
||||||
function on_open()
|
function on_open()
|
||||||
create_trackbar_setting("camera.fov", "FOV", 1, "°")
|
create_trackbar_setting("camera.fov", "FOV", 1, "°")
|
||||||
create_trackbar_setting("display.framerate", "Framerate", 1, "", "", true)
|
create_trackbar_setting("display.framerate", "Framerate", 1, "", "", true)
|
||||||
create_trackbar_setting("display.gui-scale", "GUI Scale", 1, "", "display.gui-scale.tooltip", true)
|
create_trackbar_setting("display.gui-scale", "GUI Scale", 0.5, "", "display.gui-scale.tooltip", true)
|
||||||
|
|
||||||
document.root:add(string.format(
|
document.root:add(string.format(
|
||||||
"<select context='settings' onselect='function(opt) app.set_setting(\"display.window-mode\", tonumber(opt)) end' selected='%s'>"..
|
"<select context='settings' onselect='function(opt) app.set_setting(\"display.window-mode\", tonumber(opt)) end' selected='%s'>"..
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local settings = require "core:settings_common".new(document, {})
|
local settings = require "core:settings_common".new(document, {app=app})
|
||||||
-- templates call these functions by name, so they must be global
|
-- templates call these functions by name, so they must be global
|
||||||
create_trackbar_setting = settings.create_trackbar_setting
|
create_trackbar_setting = settings.create_trackbar_setting
|
||||||
update_trackbar_label = settings.update_trackbar_label
|
update_trackbar_label = settings.update_trackbar_label
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
-- Usage in a page script:
|
-- Usage in a page script:
|
||||||
--
|
--
|
||||||
-- local settings = require "core:settings_common".new(document, {
|
-- local settings = require "core:settings_common".new(document, {
|
||||||
|
-- app = app,
|
||||||
-- tostring_overrides = {["display.framerate"] = function(x) ... end}
|
-- tostring_overrides = {["display.framerate"] = function(x) ... end}
|
||||||
-- })
|
-- })
|
||||||
-- -- templates call these functions by name, so they must be global
|
-- -- templates call these functions by name, so they must be global
|
||||||
|
|
@ -15,9 +16,11 @@ local settings_common = {}
|
||||||
--- Create helpers bound to the page document.
|
--- Create helpers bound to the page document.
|
||||||
--- @param document the page document
|
--- @param document the page document
|
||||||
--- @param options table (optional) with fields:
|
--- @param options table (optional) with fields:
|
||||||
|
--- app: the page's `app` library (not visible inside modules)
|
||||||
--- tostring_overrides: {[setting_id] = function(value) -> string}
|
--- tostring_overrides: {[setting_id] = function(value) -> string}
|
||||||
function settings_common.new(document, options)
|
function settings_common.new(document, options)
|
||||||
options = options or {}
|
options = options or {}
|
||||||
|
local app = options.app
|
||||||
local tostring_overrides = options.tostring_overrides or {}
|
local tostring_overrides = options.tostring_overrides or {}
|
||||||
local this = {}
|
local this = {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ pack.remove-confirm=Удалить весь поставляемый паком/
|
||||||
audio.acoustic-effects.tooltip=Акустические эффекты могут не поддерживаться системой
|
audio.acoustic-effects.tooltip=Акустические эффекты могут не поддерживаться системой
|
||||||
audio.volume-contrast.tooltip=Меньше - громкие звуки приглушаются до уровня тихих (ночной режим)
|
audio.volume-contrast.tooltip=Меньше - громкие звуки приглушаются до уровня тихих (ночной режим)
|
||||||
audio.recording-enabled.tooltip=Включает функции записи звука
|
audio.recording-enabled.tooltip=Включает функции записи звука
|
||||||
display.gui-scale.tooltip=Размер интерфейса: Авто или 1-4. Слишком большой масштаб уменьшается под размер окна
|
display.gui-scale.tooltip=Размер интерфейса: Авто или 0.5-4. Меню уменьшается, если не помещается в окно
|
||||||
graphics.enable-fog.tooltip=Отображать туман / дымку
|
graphics.enable-fog.tooltip=Отображать туман / дымку
|
||||||
graphics.gamma.tooltip=Кривая яркости освещения
|
graphics.gamma.tooltip=Кривая яркости освещения
|
||||||
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
|
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ void Batch2D::vertex(
|
||||||
float u, float v,
|
float u, float v,
|
||||||
float r, float g, float b, float a
|
float r, float g, float b, float a
|
||||||
) {
|
) {
|
||||||
|
if (pixelSnap > 0.0f) {
|
||||||
|
x = std::round(x * pixelSnap) / pixelSnap;
|
||||||
|
y = std::round(y * pixelSnap) / pixelSnap;
|
||||||
|
}
|
||||||
buffer[index].position = {x, y};
|
buffer[index].position = {x, y};
|
||||||
buffer[index].uv = {u * region.getWidth() + region.u1, v * region.getHeight() + region.v1};
|
buffer[index].uv = {u * region.getWidth() + region.u1, v * region.getHeight() + region.v1};
|
||||||
buffer[index].color = {r, g, b, a};
|
buffer[index].color = {r, g, b, a};
|
||||||
|
|
@ -54,6 +58,9 @@ void Batch2D::vertex(
|
||||||
glm::vec2 uvpoint,
|
glm::vec2 uvpoint,
|
||||||
float r, float g, float b, float a
|
float r, float g, float b, float a
|
||||||
) {
|
) {
|
||||||
|
if (pixelSnap > 0.0f) {
|
||||||
|
point = glm::round(point * pixelSnap) / pixelSnap;
|
||||||
|
}
|
||||||
buffer[index].position = point;
|
buffer[index].position = point;
|
||||||
buffer[index].uv = {uvpoint.x * region.getWidth() + region.u1, uvpoint.y * region.getHeight() + region.v1};
|
buffer[index].uv = {uvpoint.x * region.getWidth() + region.u1, uvpoint.y * region.getHeight() + region.v1};
|
||||||
buffer[index].color = {r, g, b, a};
|
buffer[index].color = {r, g, b, a};
|
||||||
|
|
@ -83,6 +90,10 @@ void Batch2D::setRegion(UVRegion region) {
|
||||||
this->region = region;
|
this->region = region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Batch2D::setPixelSnap(float pixelsPerUnit) {
|
||||||
|
pixelSnap = pixelsPerUnit;
|
||||||
|
}
|
||||||
|
|
||||||
void Batch2D::point(float x, float y, float r, float g, float b, float a){
|
void Batch2D::point(float x, float y, float r, float g, float b, float a){
|
||||||
if (index + 6 >= capacity)
|
if (index + 6 >= capacity)
|
||||||
flush();
|
flush();
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ class Batch2D : public Flushable {
|
||||||
const Texture* currentTexture;
|
const Texture* currentTexture;
|
||||||
DrawPrimitive primitive = DrawPrimitive::triangle;
|
DrawPrimitive primitive = DrawPrimitive::triangle;
|
||||||
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
|
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
|
||||||
|
/// @brief Vertex positions are rounded to 1/pixelSnap (0 - disabled)
|
||||||
|
float pixelSnap = 0.0f;
|
||||||
|
|
||||||
void setPrimitive(DrawPrimitive primitive);
|
void setPrimitive(DrawPrimitive primitive);
|
||||||
|
|
||||||
|
|
@ -56,6 +58,11 @@ public:
|
||||||
void texture(const Texture* texture);
|
void texture(const Texture* texture);
|
||||||
void untexture();
|
void untexture();
|
||||||
void setRegion(UVRegion region);
|
void setRegion(UVRegion region);
|
||||||
|
|
||||||
|
/// @brief Align vertices to the pixel grid (fixes artifacts of textured
|
||||||
|
/// quads on fractional UI scale)
|
||||||
|
/// @param pixelsPerUnit window pixels per one unit, 0 - disable
|
||||||
|
void setPixelSnap(float pixelsPerUnit);
|
||||||
void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint);
|
void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint);
|
||||||
void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint);
|
void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint);
|
||||||
void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint);
|
void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
|
|
@ -103,16 +104,28 @@ int GUI::calcMaxScale(const glm::uvec2& viewport) {
|
||||||
return maxFit;
|
return maxFit;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GUI::calcScale(const glm::uvec2& viewport) const {
|
float GUI::calcScale(const glm::uvec2& viewport) const {
|
||||||
int maxFit = calcMaxScale(viewport);
|
float value = engine.getSettings().display.guiScale.get();
|
||||||
int value = engine.getSettings().display.guiScale.get();
|
float result = value <= 0.0f
|
||||||
if (value <= 0) {
|
? static_cast<float>(calcMaxScale(viewport)) // auto
|
||||||
return maxFit; // auto
|
: value;
|
||||||
|
|
||||||
|
// while a menu page is open, it must fit the window
|
||||||
|
auto& page = menu->getCurrent();
|
||||||
|
if (page.panel) {
|
||||||
|
auto size = page.panel->getSize();
|
||||||
|
if (size.x > 0.0f && size.y > 0.0f) {
|
||||||
|
float fit = std::min(viewport.x / size.x, viewport.y / size.y);
|
||||||
|
fit = std::floor(fit * 2.0f) / 2.0f; // step 0.5
|
||||||
|
result = std::min(result, std::max(fit, 0.5f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return std::min(value, maxFit);
|
// keep UI size at least 1 unit
|
||||||
|
float limit = static_cast<float>(std::min(viewport.x, viewport.y));
|
||||||
|
return std::max(0.5f, std::min(result, std::max(limit, 0.5f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int GUI::getScale() const {
|
float GUI::getScale() const {
|
||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,8 +135,8 @@ int GUI::getMaxScale() const {
|
||||||
|
|
||||||
CursorState GUI::getCursor() const {
|
CursorState GUI::getCursor() const {
|
||||||
auto cursor = input.getCursor();
|
auto cursor = input.getCursor();
|
||||||
cursor.pos /= static_cast<float>(scale);
|
cursor.pos /= scale;
|
||||||
cursor.delta /= static_cast<float>(scale);
|
cursor.delta /= scale;
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +295,7 @@ void GUI::actFocused() {
|
||||||
|
|
||||||
void GUI::act(float delta, const glm::uvec2& vp) {
|
void GUI::act(float delta, const glm::uvec2& vp) {
|
||||||
scale = calcScale(vp);
|
scale = calcScale(vp);
|
||||||
container->setSize(glm::vec2(vp) / static_cast<float>(scale));
|
container->setSize(glm::vec2(vp) / scale);
|
||||||
for (auto& pair : frames) {
|
for (auto& pair : frames) {
|
||||||
pair.second->act(delta);
|
pair.second->act(delta);
|
||||||
}
|
}
|
||||||
|
|
@ -318,11 +331,11 @@ void GUI::postAct() {
|
||||||
|
|
||||||
void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
||||||
auto ctx = pctx.sub(batch2D.get());
|
auto ctx = pctx.sub(batch2D.get());
|
||||||
ctx.setUiScale(static_cast<float>(scale));
|
ctx.setUiScale(scale);
|
||||||
|
|
||||||
// viewport in UI units
|
// viewport in UI units
|
||||||
const glm::vec2 viewport =
|
const glm::vec2 viewport =
|
||||||
glm::vec2(ctx.getViewport()) / static_cast<float>(scale);
|
glm::vec2(ctx.getViewport()) / scale;
|
||||||
|
|
||||||
auto& page = menu->getCurrent();
|
auto& page = menu->getCurrent();
|
||||||
if (page.panel) {
|
if (page.panel) {
|
||||||
|
|
@ -341,6 +354,9 @@ void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
||||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||||
|
|
||||||
batch2D->begin();
|
batch2D->begin();
|
||||||
|
// fractional scale puts quad edges at pixel centers, which makes
|
||||||
|
// atlas sampling bleed into neighbour glyphs
|
||||||
|
batch2D->setPixelSnap(scale != std::floor(scale) ? scale : 0.0f);
|
||||||
for (auto& [outputTexture, frame] : frames) {
|
for (auto& [outputTexture, frame] : frames) {
|
||||||
frame->updateOutput(assets);
|
frame->updateOutput(assets);
|
||||||
frame->draw(ctx, assets);
|
frame->draw(ctx, assets);
|
||||||
|
|
@ -382,6 +398,7 @@ void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
||||||
batch2D->setColor(0, 255, 0);
|
batch2D->setColor(0, 255, 0);
|
||||||
batch2D->lineRect(pos.x, pos.y, size.x-1, size.y-1);
|
batch2D->lineRect(pos.x, pos.y, size.x-1, size.y-1);
|
||||||
}
|
}
|
||||||
|
batch2D->setPixelSnap(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<UINode> GUI::getFocused() const {
|
std::shared_ptr<UINode> GUI::getFocused() const {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ namespace gui {
|
||||||
vec2supplier cursorLocator;
|
vec2supplier cursorLocator;
|
||||||
|
|
||||||
/// @brief Current GUI scale (UI units -> window pixels)
|
/// @brief Current GUI scale (UI units -> window pixels)
|
||||||
int scale = 1;
|
float scale = 1.0f;
|
||||||
|
|
||||||
float tooltipTimer = 0.0f;
|
float tooltipTimer = 0.0f;
|
||||||
float doubleClickTimer = 0.0f;
|
float doubleClickTimer = 0.0f;
|
||||||
|
|
@ -96,8 +96,7 @@ namespace gui {
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
|
|
||||||
static int calcMaxScale(const glm::uvec2& viewport);
|
static int calcMaxScale(const glm::uvec2& viewport);
|
||||||
int calcScale(const glm::uvec2& viewport) const;
|
float calcScale(const glm::uvec2& viewport) const;
|
||||||
CursorState getScaledCursor() const;
|
|
||||||
|
|
||||||
void actMouse(Frame& frame, float delta, const CursorState& cursor);
|
void actMouse(Frame& frame, float delta, const CursorState& cursor);
|
||||||
void actFocused();
|
void actFocused();
|
||||||
|
|
@ -133,9 +132,9 @@ namespace gui {
|
||||||
void postAct();
|
void postAct();
|
||||||
|
|
||||||
/// @brief Get current GUI scale: window pixels per one UI unit
|
/// @brief Get current GUI scale: window pixels per one UI unit
|
||||||
int getScale() const;
|
float getScale() const;
|
||||||
|
|
||||||
/// @brief Get maximal GUI scale that fits the current window size
|
/// @brief Get GUI scale used in auto mode (fits the current window size)
|
||||||
int getMaxScale() const;
|
int getMaxScale() const;
|
||||||
|
|
||||||
/// @brief Get cursor state in UI coordinates
|
/// @brief Get cursor state in UI coordinates
|
||||||
|
|
|
||||||
|
|
@ -1068,7 +1068,7 @@ static int l_gui_getviewport(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_gui_get_scale(lua::State* L) {
|
static int l_gui_get_scale(lua::State* L) {
|
||||||
return lua::pushinteger(L, engine->getGUI().getScale());
|
return lua::pushnumber(L, engine->getGUI().getScale());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_gui_get_max_scale(lua::State* L) {
|
static int l_gui_get_max_scale(lua::State* L) {
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ struct DisplaySettings {
|
||||||
FlagSetting limitFpsIconified {false};
|
FlagSetting limitFpsIconified {false};
|
||||||
/// @brief Adaptive framerate in menu (experimental)
|
/// @brief Adaptive framerate in menu (experimental)
|
||||||
FlagSetting adaptiveFpsInMenu {false};
|
FlagSetting adaptiveFpsInMenu {false};
|
||||||
/// @brief GUI scale factor (0 - auto, 1..4 - integer scale limited by window size)
|
/// @brief GUI scale factor (0 - auto, 0.5..4 - explicit scale)
|
||||||
IntegerSetting guiScale {0, 0, 4};
|
NumberSetting guiScale {0.0f, 0.0f, 4.0f};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChunksSettings {
|
struct ChunksSettings {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue