---------------------------------------------------------------------------
-- Vdo_button_pc
--
-- A button designed specifically for use in the PC version only
-- The button handles only mouse inputs, not normal keyboard/gamepad inputs
-- The button's functionality can be modified to fit a few different needs:
-- * A standard button with text
-- * A standard button with an icon
-- * A toggle button with multiple possible values (2 or more)
-- * A slider with a min and max value
---------------------------------------------------------------------------
function vdo_button_pc_init()
end
function vdo_button_pc_cleanup()
end
-- Inherited from Vdo_base_object
Vdo_button_pc = Vdo_base_object:new_base()
-- Drawing sizes
local HEIGHT_ICON = 28.0
local HEIGHT_TEXT = 24.0
local HEIGHT_MINI = 16.0
local OFFSET_TEXT = 0.0
local OFFSET_BAR_VALUE = 0.0
local ARROW_PAD = 24.0
-- Button types
PC_TYPE_NORMAL = 1
PC_TYPE_ICON = 2
PC_TYPE_TOGGLE = 3
PC_TYPE_SLIDER = 4
-- Definitions for colors
-- NORMAL: Nothing special going on
-- HIGHLIGHT: Mouse over the button
-- ACTIVATE: Mouse click (NO LONGER USED)
-- INACTIVE: Button is visible but not clickable
-- OUTLINE: Slider outline
-- TEXT: Text and icon color
-- SOLID: Solid backdrop color
-- VALUE: Slider value color
-- Colors
local COLOR_SLIDER_TEXT = {R = 230/255, G=230/255, B=230/255}
local COLOR_TEXT_NORMAL = {R=160/255, G=160/255, B=160/255}
local COLOR_TEXT_HIGHLIGHT = {R=0/255, G=0/255, B=0/255}
local COLOR_TEXT_INACTIVE = {R=50/255, G=50/255, B=50/255}
local COLOR_SOLID_NORMAL = {R=0/255, G=0/255, B=0/255}
local COLOR_SOLID_HIGHLIGHT = {R=148/255, G=0/255, B=197/255}
local COLOR_SOLID_INACTIVE = {R=0/255, G=0/255, B=0/255}
local COLOR_VALUE_NORMAL = {R = 59/255, G = 54/255, B = 58/255}
local COLOR_VALUE_HIGHLIGHT = {R=90/255, G=90/255, B=90/255}
local COLOR_VALUE_INACTIVE = {R=30/255, G=27/255, B=29/255}
local COLOR_ARROW_NORMAL = {R=0/255, G=0/255, B=0/255} -- If the arrows are hard to find on sliders, change this to dark gray
local COLOR_ARROW_NO_HIGHLIGHT = {R=118/255, G=0/255, B=157/255}
-- Initialize the button
-- Sets it to the default state
function Vdo_button_pc:init()
-- Components of the button
self.bar_solid = Vdo_base_object:new("bar_solid", self.handle, self.doc_handle)
self.bar_outline = Vdo_base_object:new("bar_outline", self.handle, self.doc_handle)
self.bar_value_l = Vdo_base_object:new("bar_value_l", self.handle, self.doc_handle)
self.bar_value_r = Vdo_base_object:new("bar_value_r", self.handle, self.doc_handle)
self.img_icon = Vdo_base_object:new("img_icon", self.handle, self.doc_handle)
self.text_label = Vdo_base_object:new("text_label", self.handle, self.doc_handle)
self.text_value = Vdo_base_object:new("text_value", self.handle, self.doc_handle)
self.img_left = Vdo_base_object:new("img_left", self.handle, self.doc_handle)
self.img_right = Vdo_base_object:new("img_right", self.handle, self.doc_handle)
-- Important flags
self.type = PC_TYPE_TOGGLE
self.inactive = false
-- Button widths and height
self.width_full = 216.0
self.width_value = 108.0
self.height = HEIGHT_TEXT
-- Set the default colors
self:set_colors(false, false)
-- Hide the icon
self:set_icon(0)
end
-- Sets the label of the button
function Vdo_button_pc:set_label(new_label)
if type(new_label) == "number" then
self.text_label:set_property("text_tag_crc", new_label)
else
self.text_label:set_property("text_tag", new_label)
end
end
-- Sets the value of the button
function Vdo_button_pc:set_value(new_value)
-- if the parameter is a number, we must be using a crc instead of the actual string
if type(new_value) == "number" then
self.text_value:set_property("text_tag_crc", new_value)
else
self.text_value:set_property("text_tag", new_value)
end
end
-- Create the button based on the data given
function Vdo_button_pc:create(data)
self.type = data.type
self.height = HEIGHT_TEXT -- Only need to override for icons
-- Set the tooltip
self.tooltip = data.tooltip or ""
-- Set the text (except for icon buttons)
if data.type == PC_TYPE_ICON then
self:set_label("")
else
self:set_label(data.label)
end
-- Hide the unused elements, and collect extra data if needed, depending on the type
if data.type == PC_TYPE_NORMAL then
-- Normal buttons just show text, don't do anything special
self:hide_value_slider()
self:hide_arrows()
elseif data.type == PC_TYPE_TOGGLE then
-- Toggle buttons have a list of options and a default option index
self:hide_value_slider()
-- Get the list of options and the current option index
self.options = data.options
self.o_index = data.o_index
-- Set the value text
self:set_value(self.options[self.o_index])
elseif data.type == PC_TYPE_SLIDER then
-- Slider buttons have a min, max, step, snap, and value (all numbers)
self.s_min = data.s_min or 0
self.s_max = data.s_max or 100
self.s_step = data.s_step or 10
self.s_snap = data.s_snap or 5
self.s_value = data.s_value or 50
-- Set the value text
self:set_value(""..self.s_value) -- Concatting ensures the value is displayed correctly
self.text_value:set_color(COLOR_SLIDER_TEXT)
elseif data.type == PC_TYPE_ICON then
self:hide_value_slider()
self:hide_arrows()
-- Set up the icon and set the button height accordingly
self:set_icon(data.icon)
if data.mini then
self.height = HEIGHT_MINI
else
self.height = HEIGHT_ICON
end
end
end
-- Hides the slider elements (but not the text/arrows)
function Vdo_button_pc:hide_value_slider()
self.bar_value_l:set_visible(false)
self.bar_value_r:set_visible(false)
self.bar_outline:set_visible(false)
-- Set them all to 0 size and center them (to make sure mouse responds correctly)
self.bar_value_l:set_actual_size(0, 0)
self.bar_value_r:set_actual_size(0, 0)
self.bar_outline:set_actual_size(0, 0)
self.bar_value_l:set_anchor(0, 0)
self.bar_value_r:set_anchor(0, 0)
self.bar_outline:set_anchor(0, 0)
end
-- Hides the arrows and value text
function Vdo_button_pc:hide_arrows()
self.text_value:set_visible(false)
self.img_left:set_visible(false)
self.img_right:set_visible(false)
-- Set the text to "", set the arrows to 0 size, and center them all (to make sure mouse responds correctly)
self:set_value("")
self.img_left:set_actual_size(0, 0)
self.img_right:set_actual_size(0, 0)
self.text_value:set_anchor(0, 0)
self.img_left:set_anchor(0, 0)
self.img_right:set_anchor(0, 0)
end
-- Sets the width of the button
-- IMPORTANT: This should ALWAYS be called after create()
function Vdo_button_pc:set_width(w_full, w_value)
self.width_full = w_full
self.width_value = w_value
-- Main button backdrop
self.bar_solid:set_actual_size(self.width_full, self.height)
-- For normal and icon buttons, set value text to "" so it doesn't affect mouse collision
if self.type == PC_TYPE_NORMAL or self.type == PC_TYPE_ICON then
self.text_value:set_text("")
end
if self.type == PC_TYPE_SLIDER or self.type == PC_TYPE_TOGGLE then
-- Align label text to left
self.text_label:set_anchor(-(self.width_full / 2.0) + 2, OFFSET_TEXT)
self.text_label:set_property("auto_offset", "w")
-- Align the arrows to the sides
self.img_left:set_anchor((self.width_full / 2.0) - ARROW_PAD * 2 - self.width_value, 0)
self.img_right:set_anchor((self.width_full / 2.0), 0)
if self.type == PC_TYPE_SLIDER then
-- Align/resize slider backdrop
self.bar_outline:set_actual_size(self.width_value - 4, self.height - 4)
self.bar_outline:set_anchor(self.width_full / 2.0 - ARROW_PAD - 2, OFFSET_BAR_VALUE)
-- Align the value bars (resizing occurs elsewhere)
self.bar_value_r:set_anchor(self.width_full / 2.0 - ARROW_PAD - 6, OFFSET_BAR_VALUE)
self.bar_value_l:set_anchor((self.width_full / 2.0) - ARROW_PAD - self.width_value + 6, 0)
self:slider_scale_bar()
elseif self.type == PC_TYPE_TOGGLE then
-- Align the value text to the center of the value area
self.text_value:set_anchor(self.width_full / 2.0 - (self.width_value / 2.0) - ARROW_PAD, OFFSET_TEXT)
end
else
-- Label text, align to center
--self.text_label:set_anchor(-(self.width_full / 2.0) + 2, OFFSET_TEXT)
--self.text_label:set_property("auto_offset", "w")
self.text_label:set_anchor(0, OFFSET_TEXT)
self.text_label:set_property("auto_offset", "c")
end
end
-- Increment the index by 1 and update the value text
function Vdo_button_pc:toggle_next()
self.o_index = self.o_index + 1
if self.o_index > #self.options then
self.o_index = 1
end
self:set_value(self.options[self.o_index])
end
-- Decrement the index by 1 and update the value text
function Vdo_button_pc:toggle_prev()
self.o_index = self.o_index - 1
if self.o_index < 1 then
self.o_index = #self.options
end
self:set_value(self.options[self.o_index])
end
-- Toggle the options
function Vdo_button_pc:toggle_dir(direction)
if direction == 0 then
self:toggle_next()
else
self:toggle_prev()
end
end
-- Toggles between the ease types (changes the icon)
function Vdo_button_pc:toggle_ease()
self.icon_num = self.icon_num + 1
if self.icon_num > PC_ICON_EASE_BOTH then -- last ease icon
self.icon_num = PC_ICON_EASE_NONE -- first ease icon
end
self:set_icon(self.icon_num)
end
-- Scales the slider's images so it reflects the slider's value relative to the min/max value
-- Also positions the slider text
function Vdo_button_pc:slider_scale_bar()
local percent = (self.s_value - self.s_min) / (self.s_max - self.s_min)
-- Set the size of the gray bars so they bracket the text
local TEXT_BUFFER = 50
local width = self.width_value - TEXT_BUFFER
self.bar_value_l:set_actual_size(max(0.01, width * percent), self.height - 12)
self.bar_value_r:set_actual_size(max(0.01, width * (1.0 - percent)), self.height - 12)
-- Align the text
local text_offset = (self.width_full / 2.0) - ARROW_PAD - self.width_value -- The default position of the left side of the value bar
text_offset = text_offset + width * percent + TEXT_BUFFER * 0.5 - 1 -- Add the size of the left bar, plus center it (with a slight fudge factor)
self.text_value:set_anchor(text_offset, OFFSET_TEXT)
-- Make the text skinny (if necessary)
if self.s_value < -99 then
self.text_value:set_scale(0.6667, 0.8)
else
self.text_value:set_scale(0.8, 0.8)
end
end
function Vdo_button_pc:set_slider_value(value)
self.s_value = floor(value)
self:set_value(""..self.s_value) -- Concatting ensures the value is displayed correctly
self:slider_scale_bar()
end
-- For slider buttons, set the value based on the mouse X coordinate
function Vdo_button_pc:mouse_set_slider(mouse_x)
if self.type == PC_TYPE_SLIDER then
-- Get the screen coordinates of the slider outline bar
local slider_x, slider_y = vint_get_global_anchor(self.bar_outline.handle, self.doc_handle)
local slider_width = element_get_actual_size(self.bar_outline.handle)
local screen_w, screen_h = vint_get_screen_size()
if vint_is_std_res() then
mouse_x = (mouse_x * 640 / screen_w)
slider_width = slider_width * (0.666667)
else
mouse_x = (mouse_x * 1280 / screen_w)
end
-- Compress the area we're comparing against (FYI: anchor's on the right)
local SIDE_OFFSET = 15
local min_x = slider_x - slider_width + SIDE_OFFSET
local max_x = slider_x - SIDE_OFFSET
-- Convert the screen coordinates to a value
local new_value = ((mouse_x - min_x) / (max_x - min_x)) * (self.s_max - self.s_min) + self.s_min
-- Clamp the slider if necessary
if new_value > self.s_max then
new_value = self.s_max
elseif new_value < self.s_min then
new_value = self.s_min
end
-- Round the value to the nearest snap value (may want to round by more for certain sliders)
new_value = floor(new_value)
local over = new_value % self.s_snap
self.s_value = new_value - over
-- Set the value and update the slider
self:set_value(""..self.s_value) -- Concatting ensures the value is displayed correctly
self:slider_scale_bar()
-- Make the button stay highlighted (slight hack)
self:mouse_move()
end
end
-- Move the button by x, y
function Vdo_button_pc:move(x, y)
local cur_x, cur_y = self:get_anchor()
self:set_anchor(cur_x + x, cur_y + y)
end
-- Sets the button to inactive (it grays out, does not respond to input)
function Vdo_button_pc:set_inactive()
self.inactive = true
self:set_colors(false, false)
end
-- Sets the button to active (it responds to mouse input)
function Vdo_button_pc:set_active()
self.inactive = false
self:set_colors(false, false)
end
function Vdo_button_pc:is_active()
if (self.inactive) then
return false
else
return true
end
end
-- Adds mouse input subscriptions to the input tracker
--
-- @func_prefix Name of the screen that is currently using the hint bar
-- @input_tracker The input tracker to hold the mouse inputs events
-- @priority The priority of the input event
function Vdo_button_pc:add_mouse_inputs(func_prefix, input_tracker, add_click, priority)
if func_prefix == nil then
return
end
priority = priority or 50
local mouse_click_function = func_prefix.."_mouse_click"
local mouse_move_function = func_prefix.."_mouse_move"
if add_click == true then
input_tracker:add_mouse_input("mouse_click", mouse_click_function, priority, self.handle)
end
input_tracker:add_mouse_input("mouse_move", mouse_move_function, priority, self.handle)
-- NOTE: Current implementation is not ideal for two reasons:
-- 1) Click responds on mouse LMB up, meaning the activated coloring is currently the same as highlighting
-- Note that the rest of the UI doesn't have separate coloring, so this is ok for now
-- Really need to have LMB down and LMB up events (up triggers action, down triggers activated coloring)
-- 2) Need to add a backdrop item behind all the buttons that captures the move event
-- so the buttons don't stay highlighted after the mouse moves off
end
-- Responds to mouse clicking on a slider arrow
-- NOTE: Not called automatically, call from the screen's input handler
function Vdo_button_pc:slider_arrow_click(left)
-- Increase/decrease the value by step, and cap it
if left == true then
self:set_colors(false, true)
self.s_value = self.s_value - self.s_step
if self.s_value < self.s_min then
self.s_value = self.s_min
end
else
self:set_colors(false, true)
self.s_value = self.s_value + self.s_step
if self.s_value > self.s_max then
self.s_value = self.s_max
end
end
-- Update the visuals
self:set_value(""..self.s_value) -- Concatting ensures the value is displayed correctly
self:slider_scale_bar()
end
-- Responds the mouse moving over an arrow
-- NOTE: Not called automatically, call from the screen's input handler
function Vdo_button_pc:slider_arrow_move(left)
if left == true then
self:set_colors(true, false)
else
self:set_colors(true, false)
end
end
-- Responds to mouse over by setting the appropriate coloring
-- NOTE: Not called automatically, call from the screen's input handler
function Vdo_button_pc:mouse_move()
if self.type == PC_TYPE_SLIDER then
-- Sliders can't be clicked on normally, so they have special highlighting when not over an arrow
if self.inactive == false then
self.bar_solid:set_color(COLOR_SOLID_HIGHLIGHT)
self:set_fg_color(COLOR_TEXT_HIGHLIGHT)
self:set_arrow_color(COLOR_ARROW_NO_HIGHLIGHT)
self:set_slider_bar_color(COLOR_VALUE_HIGHLIGHT)
end
else
self:set_colors(true, false)
end
end
-- Responds to mouse clicks by setting the appropriate coloring (see NOTE above about why this isn't ideal currently)
-- NOTE: Not called automatically, call from the screen's input handler
function Vdo_button_pc:mouse_click()
self:set_colors(false, true)
end
-- Responds to mouse off by setting the appropriate coloring
function Vdo_button_pc:mouse_off()
self:set_colors(false, false)
end
-- Find the width of the label's text
function Vdo_button_pc:get_label_width()
return self.text_label:get_actual_size() + 7 -- Add a little padding
end
-- Get the width of the arrows in a toggle/slider button
function Vdo_button_pc:get_arrow_double_width()
return ARROW_PAD * 2
end
-- Find the width of the current value's text
-- NOTE: If button has multiple values, you'll need to account for that
function Vdo_button_pc:get_value_width()
if self.type == PC_TYPE_SLIDER then
return 100
else
return self.text_value:get_actual_size() + 3 -- Add some padding
end
end
-- Returns the current option index (only valid for toggles)
function Vdo_button_pc:get_option_index()
if self.type == PC_TYPE_TOGGLE then
return self.o_index
else
return 0
end
end
-- Set the option index directly (and update the value text)
function Vdo_button_pc:set_option_index(idx)
if self.type == PC_TYPE_TOGGLE then
self.o_index = idx
self:set_value(self.options[self.o_index])
end
end
function Vdo_button_pc:get_tooltip()
return self.tooltip
end
PC_ICON_PLAY = 1
PC_ICON_PLAY_SLOW = 2
PC_ICON_PLAY_FAST = 3
PC_ICON_PAUSE = 4
PC_ICON_REWIND = 5
PC_ICON_OPTIONS = 6
PC_ICON_AV_OPTIONS = 7
PC_ICON_SAVE = 8
PC_ICON_EXPORT = 9
PC_ICON_EXIT = 10
PC_ICON_LOCKED = 11
PC_ICON_UNLOCKED = 12
PC_ICON_EASE_NONE = 13 -- NOTE: Ease icons should be continuous in number (so cycling them will work)
PC_ICON_EASE_OUT = 14
PC_ICON_EASE_IN = 15
PC_ICON_EASE_BOTH = 16
-- Sets the icon for the button
-- See the if-else below to find icon numbers
function Vdo_button_pc:set_icon(icon)
self.icon_num = icon
if icon == 0 then
self.img_icon:set_visible(false)
self.text_label:set_visible(true)
self.height = HEIGHT_TEXT
else
self.text_label:set_visible(false)
self.img_icon:set_visible(true)
self.height = HEIGHT_ICON
-- Set the icon
if icon == PC_ICON_PLAY then self.img_icon:set_image("ui_pc_icon_play")
elseif icon == PC_ICON_PLAY_SLOW then self.img_icon:set_image("ui_pc_icon_play_slow") -- TODO: REPLACE THIS
elseif icon == PC_ICON_PLAY_FAST then self.img_icon:set_image("ui_pc_icon_play_fast") -- TODO: REPLACE THIS
elseif icon == PC_ICON_PAUSE then self.img_icon:set_image("ui_pc_icon_pause")
elseif icon == PC_ICON_REWIND then self.img_icon:set_image("ui_pc_icon_rewind")
elseif icon == PC_ICON_OPTIONS then self.img_icon:set_image("ui_pc_icon_options")
elseif icon == PC_ICON_AV_OPTIONS then self.img_icon:set_image("ui_pc_icon_av_options")
elseif icon == PC_ICON_SAVE then self.img_icon:set_image("ui_pc_icon_save")
elseif icon == PC_ICON_EXPORT then self.img_icon:set_image("ui_pc_icon_export")
elseif icon == PC_ICON_EXIT then self.img_icon:set_image("ui_pc_icon_exit")
elseif icon == PC_ICON_LOCKED then self.img_icon:set_image("ui_pc_icon_locked")
elseif icon == PC_ICON_UNLOCKED then self.img_icon:set_image("ui_pc_icon_unlocked")
elseif icon == PC_ICON_EASE_NONE then self.img_icon:set_image("ui_pc_icon_ease_none")
elseif icon == PC_ICON_EASE_IN then self.img_icon:set_image("ui_pc_icon_ease_in")
elseif icon == PC_ICON_EASE_OUT then self.img_icon:set_image("ui_pc_icon_ease_out")
elseif icon == PC_ICON_EASE_BOTH then self.img_icon:set_image("ui_pc_icon_ease_both")
end
end
end
-- Set the offset of the icon
function Vdo_button_pc:set_icon_offset(x, y)
self.img_icon:set_anchor(x, y)
end
-- Set the scale of the icon
function Vdo_button_pc:set_icon_scale(scale)
self.img_icon:set_scale(scale, scale)
end
-- Make the button a normal size so it fits
function Vdo_button_pc:set_icon_mid()
self.height = HEIGHT_TEXT
end
-- Make the button use the smallest height
-- Call before setting the width, since that actually resizes the button
function Vdo_button_pc:set_button_mini()
self.height = HEIGHT_MINI
end
-- Sets the colors for all the button components based on the button's state
function Vdo_button_pc:set_colors(highlighted, activated)
if self.inactive then
self.bar_solid:set_color(COLOR_SOLID_INACTIVE)
self:set_fg_color(COLOR_TEXT_INACTIVE)
self:set_arrow_color(COLOR_SOLID_INACTIVE)
self:set_slider_bar_color(COLOR_VALUE_INACTIVE)
elseif highlighted or activated then
self.bar_solid:set_color(COLOR_SOLID_HIGHLIGHT)
self:set_fg_color(COLOR_TEXT_HIGHLIGHT)
self:set_arrow_color(COLOR_TEXT_HIGHLIGHT)
self:set_slider_bar_color(COLOR_VALUE_HIGHLIGHT)
else
self.bar_solid:set_color(COLOR_SOLID_NORMAL)
self:set_fg_color(COLOR_TEXT_NORMAL)
-- Sliders and toggles have different rules for how the arrows highlight
if self.type == PC_TYPE_SLIDER then
self:set_arrow_color(COLOR_ARROW_NORMAL)
else
self:set_arrow_color(COLOR_SOLID_NORMAL)
end
self:set_slider_bar_color(COLOR_VALUE_NORMAL)
end
end
-- Sets the color of all FG objects (text and images)
function Vdo_button_pc:set_fg_color(color)
self.img_icon:set_color(color)
self.text_label:set_color(color)
if self.type ~= PC_TYPE_SLIDER then
self.text_value:set_color(color)
else
-- Sliders darken their value text when inactive
if self.inactive then
self.text_value:set_color(COLOR_VALUE_HIGHLIGHT)
else
self.text_value:set_color(COLOR_SLIDER_TEXT)
end
end
end
-- Set the color of the gray slider images on either side of the text
function Vdo_button_pc:set_slider_bar_color(color)
self.bar_value_l:set_color(color)
self.bar_value_r:set_color(color)
end
-- Sets the color of the arrows
function Vdo_button_pc:set_arrow_color(color)
self.img_left:set_color(color)
self.img_right:set_color(color)
end