Skip to content

Creating your own Widget

Loewe_111 edited this page Nov 14, 2023 · 8 revisions

This is a guide on how to create your own widget for cctinker. This guide assumes you have a basic understanding of how cctinker works.

In this Tutorial we will create a Toggle switch.

Creating the widget

Creating the function

First create a function with the like this: function screenObject:widgetName(args). This function will be called when you want to create a widget.

Then you can use the _checkArgs function to check if all required arguments are present. This isn't required, but it is recommended.

function screenObject:toggleSwitch(args)
  self:_checkArgs(args, {"x", "y", "text"})
end

Creating the widget table

The first argument of the _checkArgs() function is the table of arguments, the second is a table of required arguments. If any of the required arguments are missing, an error will be thrown.

Now you can create the widget. The first thing you should do is create a table for the widget. This table will be used to store the widget's state. You can store anything in this table, but it is recommended to store the widget's position, size and text with the shown names.

The following values are required for a widget to work:

type = "widgetType", -- The type of the widget, this value is required
id = "widgetId", -- The id of the widget, this value is required
x = 1, -- The x position of the widget
y = 1, -- The y position of the widget
width = 1, -- The width of the widget
height = 1, -- The height of the widget

Using this on our example widget, we get the following:

function screen:toggleSwitch(args)
  self:_checkArgs(args, {"x", "y", "text"}) -- Error if required args are missing
  local switchObject = {
    type = "toggleSwitch", -- The type of the widget, this value is required
    id = args.id or self:_generateId(), -- The id of the widget, this value is required
    x = args.x,
    y = args.y,
    width = #args.text + 4, -- The width of the widget is the length of the text + 4
    height = 1, -- The height of the widget is 1
    text = args.text,
    color = args.color or colors.white, -- The color of the text, default is white
    background = args.background or colors.black, -- The background color, default is black
    state = args.state or false, -- The state of the switch, default is off
  }
end

Clicking the widget / Events

Widgets can subscribe to events using functions. The function will be called when the event is emitted.

To subscribe to add one of the following functions to your widget:

event_click(x, y, button)

Called when the user clicks the mouse on the widget.

event_defocus(x, y, button)

Called when the user clicks the mouse on the screen outside the widgetd.

event_scroll(direction)

Called when the user scrolls the mouse wheel while the mouse is over the widget.

event_char(char)

Called when the user types a character on the keyboard.

event_key(key, keycode, holding)

Called when a key is pressed.

event_paste(text)

Called when the user pastes text into the widget.

Meaning If your widget be clicked, you need to add a event_click function to the widget table. This function will be called with the arguments x, y, button when the widget is clicked. X and Y beeing the position of the click, and button the mouse button that was clicked.

As our example widget is a toggle switch, we want to change the state when it is clicked. We can do this by changing the state value in the widget table.

You should also call the callback function from the arguments, if it is present.

function screen:toggleSwitch(args)
  self:_checkArgs(args, {"x", "y", "text"}) -- Error if required args are missing
  local switchObject = {
    type = "toggleSwitch", -- The type of the widget, this value is required
    id = args.id or self:_generateId(), -- The id of the widget, this value is required
    x = args.x,
    y = args.y,
    width = #args.text + 4, -- The width of the widget is the length of the text + 4
    height = 1, -- The height of the widget is 1
    text = args.text,
    color = args.color or colors.white, -- The color of the text, default is white
    background = args.background or colors.black, -- The background color, default is black
    state = args.state or false, -- The state of the switch, default is off
  }
  switchObject.event_click = function(x, y, button)
    switchObject.state = not switchObject.state -- Toggle the state
    if args.callback then
      args.callback(x, y, button, switchObject.state) -- Call the callback function if it exists
    end
  end
end

Drawing the widget

Now we need to draw the widget. We can do this by adding a draw function to the widget table.

Not all the normal term functions are available for widgets. The following functions are available:

  • write(text)
  • blit(text, textColor, backgroundColor)
  • getCursorPos() -> x, y
  • setCursorPos(x, y)
  • getTextColor() -> color
  • setTextColor(color)
  • getBackgroundColor() -> color
  • setBackgroundColor(color)

The setCursorBlink function works a bit different from the normal function from the term object, it takes three arguments now: setCursorBlink(blink, x, y) blink say if the blink is enabled and x, y specifies the position of the blinking cursor.

The functions get called directly on self like this: self:write("Hello, World!")

function screen:toggleSwitch(args)
  self:_checkArgs(args, {"x", "y", "text"}) -- Error if required args are missing
  local switchObject = {
    type = "toggleSwitch", -- The type of the widget, this value is required
    id = args.id or self:_generateId(), -- The id of the widget, this value is required
    x = args.x,
    y = args.y,
    width = #args.text + 4, -- The width of the widget is the length of the text + 4
    height = 1, -- The height of the widget is 1
    text = args.text,
    color = args.color or colors.white, -- The color of the text, default is white
    background = args.background or colors.black, -- The background color, default is black
    state = args.state or false, -- The state of the switch, default is off
  }
  switchObject.click = function(x, y, button)
    switchObject.state = not switchObject.state -- Toggle the state
    if args.callback then
      args.callback(x, y, button, switchObject.state) -- Call the callback function if it exists
    end
  end
  switchObject.draw = function()
    self.term.setCursorPos(switchObject.x, switchObject.y) -- Set the cursor position to the x and y of the widget
    if switchObject.state then -- If the state is true, draw the switch in the on position
      self:setTextColor(colors.white)
      self:setBackgroundColor(colors.lime)
      self:write("  |")
    else -- If the state is false, draw the switch in the off position
      self:setTextColor(colors.white)
      self:setBackgroundColor(colors.red)
      self:write("|  ")
    end
    self:setTextColor(switchObject.color)
    self:setBackgroundColor(switchObject.background)
    self:write(" " .. switchObject.text) -- Draw the text
  end
end

Adding the widget to the screen

Now we need to add the widget to the screen. We can do this by adding the widget table to the screenObjects table, which stores all currently displayed screenObjects. Then return the widget table.

This is the final code for our widget:

function screen:toggleSwitch(args)
  self:_checkArgs(args, {"x", "y", "text"}) -- Error if required args are missing
  local switchObject = {
    type = "toggleSwitch", -- The type of the widget, this value is required
    id = args.id or self:_generateId(), -- The id of the widget, this value is required
    x = args.x,
    y = args.y,
    width = #args.text + 4, -- The width of the widget is the length of the text + 4
    height = 1, -- The height of the widget is 1
    text = args.text,
    color = args.color or colors.white, -- The color of the text, default is white
    background = args.background or colors.black, -- The background color, default is black
    state = args.state or false, -- The state of the switch, default is off
  }
  switchObject.click = function(x, y, button)
    switchObject.state = not switchObject.state -- Toggle the state
    if args.callback then
      args.callback(x, y, button, switchObject.state) -- Call the callback function if it exists
    end
  end
  switchObject.draw = function()
    self.term.setCursorPos(switchObject.x, switchObject.y) -- Set the cursor position to the x and y of the widget
    if switchObject.state then -- If the state is true, draw the switch in the on position
      self:setTextColor(colors.white)
      self:setBackgroundColor(colors.lime)
      self:write("  |")
    else -- If the state is false, draw the switch in the off position
      self:setTextColor(colors.white)
      self:setBackgroundColor(colors.red)
      self:write("|  ")
    end
    self:setTextColor(switchObject.color)
    self:setBackgroundColor(switchObject.background)
    self:write(" " .. switchObject.text) -- Draw the text
  end
  
  self.screenObjects[switchObject.id] = switchObject -- Add the widget to the screenObjects table
  return switchObject -- Return the widget
end

Using the widget

You can use the widget like any other widget.

local switch = screen:toggleSwitch({x = 1, y = 1, text = "Toggle"})

This is the result:

grafik