Last active
May 8, 2022 13:21
-
-
Save williammustaffa/9e22bb1bdc23e6fb9823addf3649a32a to your computer and use it in GitHub Desktop.
Simple CSS-Like Game Maker Studio 2 UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
varying vec2 v_vTexcoord; | |
varying vec4 v_vColour; | |
varying vec3 v_vPosition; | |
uniform vec4 u_bounds; | |
void main() { | |
vec4 color = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord); | |
// calculate alpha | |
color.a *= float( | |
v_vPosition.x >= u_bounds[0] && | |
v_vPosition.y >= u_bounds[1] && | |
v_vPosition.x < u_bounds[2] && | |
v_vPosition.y < u_bounds[3] | |
); | |
gl_FragColor = color; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
attribute vec3 in_Position; | |
attribute vec4 in_Colour; | |
attribute vec2 in_TextureCoord; | |
varying vec2 v_vTexcoord; | |
varying vec4 v_vColour; | |
varying vec3 v_vPosition; | |
void main() { | |
v_vPosition = in_Position; | |
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position.x, in_Position.y, in_Position.z, 1.0); | |
v_vColour = in_Colour; | |
v_vTexcoord = in_TextureCoord; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function UIElement() constructor { | |
// General | |
debug = false; | |
precise = true; | |
// Rendering | |
overflow = "visible"; | |
overflow_x = undefined; | |
overflow_y = undefined; | |
// Text | |
opacity = 1; | |
color = make_color_rgb(255, 211, 163); | |
color_hover = make_color_rgb(191, 38, 81); | |
font_family = fnt_default; | |
hoverable = false; | |
// Position | |
orientation = "horizontal"; // Accepts "horizontal" and "vertical" | |
position = "relative"; | |
left = 0; | |
top = 0; | |
vertical_align = "top"; // Accepts "top"; "middle"; "bottom" | |
horizontal_align = "left"; // Accepts "left"; "middle"; "right" | |
// Dimensions | |
height = "auto"; // Accepts "auto" and numbers | |
width = "auto"; // Accepts "auto" and numbers | |
max_width = display_get_gui_width(); | |
max_height = display_get_gui_height(); | |
min_width = 0; | |
min_height = 0; | |
// Padding | |
padding = 0; | |
padding_bottom = undefined; | |
padding_top = undefined; | |
padding_left = undefined; | |
padding_right = undefined; | |
// Margin | |
margin = 0; | |
margin_bottom = undefined; | |
margin_top = undefined; | |
margin_left = undefined; | |
margin_right = undefined; | |
// Background | |
background = undefined; | |
background_opacity = 1; | |
background_surface = undefined; | |
// Listeners | |
onclick = undefined; | |
onmousein = undefined; | |
onmouseout = undefined; | |
onscroll = undefined; | |
// Scroll | |
scrollbar_width = 10; | |
scroll_speed = 40; | |
scrollbar_background = c_dkgray; | |
scrollbar_color = c_ltgray; | |
// Control variables | |
_nodes = []; | |
_text = undefined; | |
_parent = undefined; | |
_computed = false; | |
_top = 0; | |
_left = 0; | |
_offset_left = 0; | |
_offset_top = 0; | |
_scroll_offset_top = 0; | |
_scroll_offset_left = 0; | |
_margin_left = 0; | |
_margin_right = 0; | |
_margin_top = 0; | |
_margin_bottom = 0; | |
_padding_left = 0; | |
_padding_right = 0; | |
_padding_top = 0; | |
_padding_bottom = 0; | |
_horizontal_spacing = 0; | |
_vertical_spacing = 0; | |
_content_width = 0; | |
_content_height = 0; | |
_width = 0; | |
_height = 0; | |
_max_width = 0; | |
_max_height = 0; | |
_top_stacked = 0; | |
_left_stacked = 0; | |
_highest_node_width = 0; | |
_highest_node_height = 0; | |
_color = 0; | |
_separation = 0; | |
_mousein = false; | |
_shader_enabled = false; | |
_overflow_x = undefined; | |
_overflow_y = undefined; | |
_overflow_hide_x = undefined; | |
_overflow_hide_y = undefined; | |
_scrollbar_horizontal_enabled = false; | |
_scrollbar_horizontal_pointer_position = 0; | |
_scrollbar_horizontal_pointer_height = 0; | |
_scrollbar_vertical_enabled = false; | |
_scrollbar_vertical_pointer_position = 0; | |
_scrollbar_vertical_pointer_height = 0; | |
static clear = function () { | |
_nodes = []; | |
} | |
static create = function (__text) { | |
var __node = new UIElement(); | |
__node._parent = self; | |
__node.debug = debug; | |
if (!is_undefined(__text)) { | |
__node._text = __text; | |
} | |
array_push(_nodes, __node); | |
return __node; | |
} | |
static update = function () { | |
_compute_reset(); | |
_compute_font(); | |
_compute_spacing(); | |
_compute_text(); | |
_compute_children(); | |
_compute_dimensions(); | |
_compute_position(); | |
_compute_alignment(); | |
_compute_events(); | |
_compute_scrollbar(); | |
_computed = true; | |
} | |
static draw = function () { | |
// Still computing data, so do nothing | |
if (!_computed) return; | |
// Render | |
_render_prepare(); | |
_render_background(); | |
_render_text(); | |
_render_children(); | |
_render_scrollbars(); | |
_render_debug(); | |
_render_finish(); | |
} | |
static getLeftPosition = function () { | |
var __left = _left; | |
if (!is_undefined(_parent) and position == "relative") { | |
__left += _parent._offset_left; | |
__left += _parent._scroll_offset_left; | |
} | |
return __left; | |
} | |
static getTopPosition = function () { | |
var __top = _top; | |
if (!is_undefined(_parent) and position == "relative") { | |
__top += _parent._offset_top; | |
__top += _parent._scroll_offset_top; | |
} | |
return __top; | |
} | |
static _render_prepare = function () { | |
draw_set_font(font_family); | |
if (_overflow_hide_x || _overflow_hide_y) { | |
// Enables shader and set flag to true indicating it should be disabled later | |
shader_set(sh_element_clip); | |
_shader_enabled = true; | |
// Get uniforms from shader | |
var __u_bounds = shader_get_uniform(sh_element_clip, "u_bounds"); | |
var __left = getLeftPosition(); | |
var __top = getTopPosition(); | |
shader_set_uniform_f( | |
__u_bounds, | |
__left + _margin_left, | |
__top + _margin_top, | |
__left + _width - _margin_right, | |
__top + _height - _margin_bottom, | |
); | |
} | |
} | |
static _render_background = function () { | |
var __left = getLeftPosition(); | |
var __top = getTopPosition(); | |
if (!is_undefined(background_surface) and surface_exists(background_surface)) { | |
draw_surface_part_ext(background_surface, 0, 0, _width, _height, __left, __top, 1, 1, c_white, 1); | |
} | |
if (!is_undefined(background)) { | |
draw_set_alpha(background_opacity); | |
draw_rectangle_color( | |
__left + _margin_left, | |
__top + _margin_top, | |
__left + _width - _margin_right, | |
__top + _height - _margin_bottom, | |
background, | |
background, | |
background, | |
background, | |
0 | |
); | |
draw_set_alpha(1); | |
} | |
} | |
static _render_text = function () { | |
if (!is_undefined(_text)) { | |
draw_text_ext_color( | |
_offset_left, | |
_offset_top, | |
_text, | |
_separation, | |
_width - _horizontal_spacing, | |
_color, | |
_color, | |
_color, | |
_color, | |
opacity | |
); | |
} | |
} | |
static _render_children = function () { | |
// Run update on child nodes, | |
// This will run compute for all childrens | |
for (var __index = 0; __index < array_length(_nodes); __index++) { | |
var __node = _nodes[__index]; | |
__node.draw(); | |
} | |
} | |
static _render_scrollbar = function (__orientation) { | |
var __left = getLeftPosition(); | |
var __top = getTopPosition(); | |
// Get positions | |
var __sb_left, __sb_top, __sb_width, __sb_height, | |
__sbp_left, __sbp_top, __sbp_width, __sbp_height; | |
if (__orientation == "horizontal") { | |
__sb_left = __left; | |
__sb_top = __top + _height - scrollbar_width; | |
__sb_width = _width; | |
__sb_height = scrollbar_width; | |
__sbp_left = __left + _scrollbar_horizontal_pointer_position; | |
__sbp_top = __top + _height - scrollbar_width; | |
__sbp_width = _scrollbar_horizontal_pointer_height; | |
__sbp_height = scrollbar_width; | |
} | |
if (__orientation == "vertical") { | |
__sb_left = __left + _width - scrollbar_width; | |
__sb_top = __top; | |
__sb_width = scrollbar_width; | |
__sb_height = _height; | |
__sbp_left = __left + _width - scrollbar_width; | |
__sbp_top = __top + _scrollbar_vertical_pointer_position; | |
__sbp_width = scrollbar_width; | |
__sbp_height = _scrollbar_vertical_pointer_height; | |
} | |
draw_rectangle_color( | |
__sb_left, | |
__sb_top, | |
__sb_left + __sb_width, | |
__sb_top + __sb_height, | |
scrollbar_background, | |
scrollbar_background, | |
scrollbar_background, | |
scrollbar_background, | |
0 | |
); | |
draw_rectangle_color( | |
__sbp_left, | |
__sbp_top, | |
__sbp_left + __sbp_width, | |
__sbp_top + __sbp_height, | |
scrollbar_color, | |
scrollbar_color, | |
scrollbar_color, | |
scrollbar_color, | |
0 | |
); | |
} | |
static _render_scrollbars = function () { | |
if (_scrollbar_horizontal_enabled) { | |
_render_scrollbar("horizontal"); | |
} | |
if (_scrollbar_vertical_enabled) { | |
_render_scrollbar("vertical"); | |
} | |
} | |
static _render_finish = function () { | |
if (_shader_enabled) { | |
shader_reset(); | |
} | |
} | |
static _render_debug = function () { | |
if (debug) { | |
var __left = getLeftPosition(); | |
var __top = getTopPosition(); | |
// Debug container space | |
draw_rectangle_color( | |
__left, | |
__top, | |
__left + _width, | |
__top + _height , | |
c_red, | |
c_red, | |
c_red, | |
c_red, | |
1 | |
); | |
// Debug margin | |
draw_rectangle_color( | |
__left + _margin_left , | |
__top + _margin_top, | |
__left + _width - _margin_right , | |
__top + _height - _margin_bottom, | |
c_green, | |
c_green, | |
c_green, | |
c_green, | |
1 | |
); | |
// Debug padding | |
draw_rectangle_color( | |
__left + _margin_left + _padding_left, | |
__top + _margin_top + _padding_top, | |
__left + _width - _margin_right - _padding_right, | |
__top + _height - _margin_bottom - _padding_bottom, | |
c_yellow, | |
c_yellow, | |
c_yellow, | |
c_yellow, | |
1 | |
); | |
// Debug content | |
draw_rectangle_color( | |
_offset_left, | |
_offset_top, | |
_offset_left + _content_width, | |
_offset_top + _content_height, | |
c_purple, | |
c_purple, | |
c_purple, | |
c_purple, | |
1 | |
); | |
} | |
} | |
static _compute_font = function () { | |
draw_set_font(font_family); | |
_color = color; | |
_separation = string_height("dummy"); | |
} | |
static _compute_reset = function () { | |
// Reset stacking | |
_left_stacked = 0; | |
_top_stacked = 0; | |
_highest_node_width = 0; | |
_highest_node_height = 0; | |
// Reset positioning | |
_left = left; | |
_top = top; | |
// Reset dimension | |
//_width = 0; | |
//_height = 0; | |
_content_width = 0; | |
_content_height = 0; | |
} | |
static _compute_spacing = function () { | |
// Compute margin | |
_margin_left = is_undefined(margin_left) ? margin : margin_left; | |
_margin_right = is_undefined(margin_right) ? margin : margin_right; | |
_margin_top = is_undefined(margin_top) ? margin : margin_top; | |
_margin_bottom = is_undefined(margin_bottom) ? margin : margin_bottom; | |
// Compute padding | |
_padding_left = is_undefined(padding_left) ? padding : padding_left ; | |
_padding_right = is_undefined(padding_right) ? padding : padding_right; | |
_padding_top = is_undefined(padding_top) ? padding : padding_top; | |
_padding_bottom = is_undefined(padding_bottom) ? padding : padding_bottom; | |
// Compute spacing | |
_horizontal_spacing = _margin_left + _padding_left + _margin_right + _padding_right; | |
_vertical_spacing = _margin_top + _padding_top + _margin_bottom + _padding_bottom; | |
// Compute overflow | |
_overflow_x = is_undefined(overflow_x) ? overflow : overflow_x; | |
_overflow_y = is_undefined(overflow_y) ? overflow : overflow_y; | |
_overflow_hide_x = _overflow_x == "hidden" or _overflow_x == "scroll"; | |
_overflow_hide_y = _overflow_y == "hidden" or _overflow_y == "scroll"; | |
// Compute max with | |
// If width or height is auto, then we rely on max_width and max_height | |
// Otherwise we set max height and max width to container width | |
_max_width = is_real(width) ? width : max_width; | |
_max_height = is_real(height) ? height : max_height; | |
// Get max width that should be the min value found as a limit | |
if (position == "relative" and !is_undefined(_parent) and _parent.width != "auto") { | |
_max_width = min(_parent._max_width - _parent._horizontal_spacing, _max_width); | |
} | |
} | |
static _compute_text = function () { | |
if (!is_undefined(_text)) { | |
var __text_width = string_width_ext(_text, _separation, _max_width - _horizontal_spacing); | |
var __text_height = string_height_ext(_text, _separation, _max_width - _horizontal_spacing); | |
_content_width += __text_width; | |
if (orientation == "horizontal") _left_stacked += __text_width; | |
_content_height += __text_height; | |
if (orientation == "vertical") _top_stacked += __text_height; | |
} | |
} | |
static _compute_children = function () { | |
for (var __index = 0; __index < array_length(_nodes); __index++) { | |
var __node = _nodes[__index]; | |
__node.update(); | |
} | |
} | |
static _compute_dimensions = function () { | |
// Compute node width | |
switch(width) { | |
case "max": | |
_width = _max_width; | |
break; | |
default: | |
if (is_real(width)) { | |
_width = width; | |
} else { | |
_width = _content_width + _horizontal_spacing; | |
}; | |
} | |
switch(height) { | |
case "max": | |
_height = _max_height; | |
break; | |
default: | |
if (is_real(height)) { | |
_height = height; | |
} else { | |
// Auto | |
_height = _content_height + _vertical_spacing; | |
}; | |
} | |
// Apply min width and min height | |
_width = max(_width, min_width); | |
_height = max(_height, min_height); | |
// Check if should enable scrollbar | |
_scrollbar_horizontal_enabled = _overflow_x == "scroll" and _content_width > _width; | |
_scrollbar_vertical_enabled = _overflow_y == "scroll" and _content_height > _height; | |
} | |
static _compute_position = function () { | |
if (position == "relative" and !is_undefined(_parent)) { | |
if (_parent.orientation == "horizontal") { | |
if (_parent._left_stacked + _width > _parent._max_width - _parent._horizontal_spacing) { | |
_parent._top_stacked += _parent._highest_node_height; | |
_parent._highest_node_height = 0; | |
_parent._left_stacked = 0; | |
} | |
_parent._highest_node_height = max(_parent._highest_node_height, _height); | |
// Update position relative to parent | |
_left += _parent._left_stacked; | |
_top += _parent._top_stacked; | |
// Increase width | |
_parent._left_stacked += _width; | |
} | |
if (_parent.orientation == "vertical") { | |
if (_parent._top_stacked + _height > _parent._max_height - _parent._vertical_spacing) { | |
_parent._left_stacked += _parent._highest_node_width; | |
_parent._top_stacked = 0; | |
_parent._highest_node_width = 0; | |
} | |
_parent._highest_node_width = max(_parent._highest_node_width, _width); | |
// Update position relative to parent | |
_left += _parent._left_stacked; | |
_top += _parent._top_stacked; | |
// Increase height | |
_parent._top_stacked += _height; | |
} | |
// Update parent content width | |
var __target_width = _parent._left_stacked | |
if (_parent.orientation == "vertical") { | |
__target_width += _parent._highest_node_width | |
}; | |
if (__target_width > _parent._content_width) { | |
_parent._content_width = __target_width; | |
} | |
// Update parent content height | |
var __target_height = _parent._top_stacked; | |
if (_parent.orientation == "horizontal") { | |
__target_height += _parent._highest_node_height; | |
} | |
if (__target_height > _parent._content_height) { | |
_parent._content_height = __target_height; | |
} | |
} | |
} | |
static _compute_alignment = function () { | |
if (horizontal_align = "left") { | |
_offset_left = _left + _padding_left + _margin_left; | |
} | |
if (horizontal_align = "center") { | |
_offset_left = _left + _width / 2 - _content_width / 2; | |
} | |
if (horizontal_align = "right") { | |
_offset_left = _left + _width - _content_width - _padding_right - margin_right; | |
} | |
if (vertical_align = "top") { | |
_offset_top = _top + _padding_top + _margin_top; | |
} | |
if (vertical_align = "center") { | |
_offset_top = _top + _height / 2 - _content_height / 2; | |
} | |
if (vertical_align = "bottom") { | |
_offset_top = _top + _height - _content_height - _padding_bottom - _margin_bottom; | |
} | |
if (!is_undefined(_parent) and position = "relative") { | |
_offset_left += _parent._offset_left; | |
_offset_top += _parent._offset_top; | |
} | |
// This is not so performatic but it ensures that children position | |
// Will be updated each step. Negative point is that this method will run more times | |
// exponentially depending on the number of sub nodes | |
if (precise) { | |
for (var __index = 0; __index < array_length(_nodes); __index++) { | |
var __node = _nodes[__index]; | |
__node._compute_alignment(); | |
} | |
} | |
} | |
static _compute_events = function () { | |
var __left = getLeftPosition(); | |
var __top = getTopPosition(); | |
// Event handling | |
if (point_in_rectangle( | |
window_mouse_get_x(), | |
window_mouse_get_y(), | |
__left + _margin_left, | |
__top + _margin_top, | |
__left + _width - _margin_right, | |
__top + _height - _margin_bottom | |
)) { | |
// Execute onmousein event | |
if (_mousein == false) { | |
_mousein = true; | |
_execute_callback(onmousein); | |
} | |
// Execute onclick event | |
if (mouse_check_button_pressed(mb_left)) { | |
_execute_callback(onclick); | |
} | |
// Vertical scrolling | |
if (!keyboard_check(vk_shift) && _scrollbar_vertical_enabled) { | |
var __scroll_step = mouse_wheel_up() - mouse_wheel_down(); | |
if (__scroll_step != 0) { | |
var __scroll_speed = scroll_speed; | |
var __target_scroll_offset_top = _scroll_offset_top + (__scroll_step * __scroll_speed); | |
_scroll_offset_top = clamp(__target_scroll_offset_top, _height - _content_height - _vertical_spacing, 0); | |
_execute_callback(onscroll); | |
} | |
} | |
// Horizontal scrolling | |
if (keyboard_check(vk_shift) && _scrollbar_horizontal_enabled) { | |
var __scroll_step = mouse_wheel_up() - mouse_wheel_down(); | |
if (__scroll_step != 0) { | |
var __scroll_speed = scroll_speed; | |
var __target_scroll_offset_left = _scroll_offset_left + (__scroll_step * __scroll_speed); | |
_scroll_offset_left = clamp(__target_scroll_offset_left, _width - _content_width - _horizontal_spacing, 0); | |
_execute_callback(onscroll); | |
} | |
} | |
if (hoverable) { | |
_color = color_hover; | |
} | |
} else { | |
if (_mousein) { | |
_mousein = false; | |
_execute_callback(onmouseout); | |
} | |
} | |
} | |
static _compute_scrollbar = function () { | |
var __total_width = _width; | |
if (_scrollbar_vertical_enabled) { | |
var __total_height = _height - (_scrollbar_horizontal_enabled ? scrollbar_width : 0); | |
var __sections = (_content_height + _vertical_spacing) / __total_height; | |
// Compute position | |
_scrollbar_vertical_pointer_height = __total_height / __sections; | |
_scrollbar_vertical_pointer_position = abs(_scroll_offset_top / __sections); | |
} | |
if (_scrollbar_horizontal_enabled) { | |
var __total_width = _width - (_scrollbar_vertical_enabled ? scrollbar_width : 0); | |
var __sections = (_content_width + _horizontal_spacing) / __total_width; | |
// Compute position | |
_scrollbar_horizontal_pointer_height = __total_width / __sections; | |
_scrollbar_horizontal_pointer_position = abs(_scroll_offset_left / __sections); | |
} | |
} | |
static _execute_callback = function (__callback) { | |
if (!is_undefined(__callback)) { | |
if (typeof(__callback) == "method") __callback(self); | |
if (script_exists(__callback)) script_execute(__callback); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment