roleplay/client/script/color_picker.lua

205 lines
5.7 KiB
Lua

-- Color picker logic, ported from the C callbacks that used to live in editor.c
--
-- Layer 0 elements:
-- 0 background
-- 1 sv square (HSV rect, hue in color channel 0)
-- 2 hue bar (HSV rect)
-- 3 sv select outline
-- 4 sv select (HSV rect)
-- 5 hex string input area
-- 6 hue select bar
-- 7-18 saved color slots
local state = {
hsv = {0, 0, 0},
rgb = {0, 0, 0, 1},
saved = {},
hex_string = "#000000FF",
hex_len = 8,
}
for i = 1, 12 do state.saved[i] = {0, 0, 0, 0} end
local function update_hex_string()
state.hex_string = string.format("#%02X%02X%02X%02X",
math.floor(state.rgb[1]*255 + 0.5),
math.floor(state.rgb[2]*255 + 0.5),
math.floor(state.rgb[3]*255 + 0.5),
math.floor(state.rgb[4]*255 + 0.5))
state.hex_len = 8
ui.set_string(0, 0, state.hex_string)
end
local function sv_pick(s, v)
s = math.max(0, math.min(1, s))
v = math.max(0, math.min(1, v))
state.hsv[2] = s
state.hsv[3] = v
ui.set_pos(0, 4, s*130 - 2, 130 - v*130 - 2)
for corner = 0, 3 do
ui.set_corner_color(0, 4, corner, state.hsv[1], s, v, 1)
end
ui.set_pos(0, 3, s*130 - 3, 130 - v*130 - 3)
state.rgb[1], state.rgb[2], state.rgb[3] =
ui.hsv_to_rgb(state.hsv[1], state.hsv[2], state.hsv[3])
update_hex_string()
end
local function hue_set(h)
h = math.max(0, math.min(1, h))
state.hsv[1] = h
-- sv square and sv select keep their per-corner s/v, only hue changes
for _, drawable in ipairs({1, 4}) do
for corner = 0, 3 do
local _, s, v, a = ui.get_corner_color(0, drawable, corner)
ui.set_corner_color(0, drawable, corner, h, s, v, a)
end
end
ui.set_pos(0, 6, 134, 2 + h*130)
state.rgb[1], state.rgb[2], state.rgb[3] =
ui.hsv_to_rgb(state.hsv[1], state.hsv[2], state.hsv[3])
update_hex_string()
end
local function hex_string_highlight(value)
for corner = 0, 3 do
local r, g, _, _ = ui.get_corner_color(0, 5, corner)
ui.set_corner_color(0, 5, corner, r, g, value, value)
end
end
local function set_saved(index, color)
state.saved[index] = {color[1], color[2], color[3], color[4]}
local element = 6 + index
for corner = 0, 3 do
local _, _, _, a = ui.get_corner_color(0, element, corner)
ui.set_corner_color(0, element, corner, color[1], color[2], color[3], a)
end
end
function on_button(layer, element, x, y, button, action, mods)
if element == 1 then -- sv square
if action == PRESS and button == MOUSE_LEFT then
ui.focus(layer, element)
sv_pick(x, 1 - y)
elseif action == RELEASE and button == MOUSE_LEFT then
ui.blur()
end
return true
elseif element == 2 then -- hue bar
if action == PRESS and button == MOUSE_LEFT then
ui.focus(layer, element)
hue_set(y)
elseif action == RELEASE and button == MOUSE_LEFT then
ui.blur()
end
return true
elseif element == 5 then -- hex string input
if action == PRESS and button == MOUSE_LEFT then
ui.focus(layer, element)
hex_string_highlight(1)
end
return true
elseif element >= 7 and element <= 18 then -- saved color slots
local index = element - 6
if action == PRESS then
if button == MOUSE_LEFT then
local saved = state.saved[index]
state.rgb = {saved[1], saved[2], saved[3], saved[4]}
state.hsv[1], state.hsv[2], state.hsv[3] =
ui.rgb_to_hsv(state.rgb[1], state.rgb[2], state.rgb[3])
hue_set(state.hsv[1])
sv_pick(state.hsv[2], state.hsv[3])
elseif button == MOUSE_RIGHT then
set_saved(index, state.rgb)
elseif button == MOUSE_MIDDLE then
set_saved(index, {0, 0, 0, 0})
end
end
return true
end
return false
end
function on_cursor(layer, element, x, y)
if ui.is_focused(0, 1) then -- dragging the sv square
sv_pick(x, 1 - y)
return true
elseif ui.is_focused(0, 2) then -- dragging the hue bar
hue_set(y)
return true
end
return false
end
function on_scroll(layer, element, x, y)
if element == 2 then -- hue bar
hue_set(state.hsv[1] + y*0.01)
return true
end
return false
end
function on_key(layer, element, key, action, mods)
if element ~= 5 then return false end
if action == PRESS then
if key == KEY_ESCAPE then
state.rgb[1], state.rgb[2], state.rgb[3] =
ui.hsv_to_rgb(state.hsv[1], state.hsv[2], state.hsv[3])
update_hex_string()
ui.blur()
elseif key == KEY_ENTER then
local s = state.hex_string
state.rgb[1] = (tonumber(s:sub(2, 3), 16) or 0) / 255
state.rgb[2] = (tonumber(s:sub(4, 5), 16) or 0) / 255
state.rgb[3] = (tonumber(s:sub(6, 7), 16) or 0) / 255
state.rgb[4] = (tonumber(s:sub(8, 9), 16) or 0) / 255
state.hsv[1], state.hsv[2], state.hsv[3] =
ui.rgb_to_hsv(state.rgb[1], state.rgb[2], state.rgb[3])
hue_set(state.hsv[1])
sv_pick(state.hsv[2], state.hsv[3])
ui.blur()
elseif key == KEY_BACKSPACE then
if state.hex_len > 0 then
state.hex_string = state.hex_string:sub(1, state.hex_len)
state.hex_len = state.hex_len - 1
ui.set_string(0, 0, state.hex_string)
end
end
end
-- consume all keys while the hex input is focused
return true
end
function on_text(layer, element, codepoint)
if element ~= 5 then return false end
if codepoint < 128 then
local ch = string.char(codepoint):upper()
if ch:match("[0-9A-F]") and state.hex_len < 8 then
state.hex_string = state.hex_string .. ch
state.hex_len = state.hex_len + 1
ui.set_string(0, 0, state.hex_string)
end
end
return true
end
function on_deselect(layer, element)
if element == 5 then
hex_string_highlight(0)
end
end
-- runs once at container load
ui.set_string(0, 0, state.hex_string)