Class ui.Custom

Custom widget.

When the none of the standard widgets suits you, you can build your own custom widget.

You yourself decide how to draw it on the screen and how it will respond to keyboard and mouse events.

For a sample script that uses a custom widget, see ui_canvas.mcs.

There are two ways to use ui.Custom. You can instantiate it and use it outright, as shown on these pages, or you can subclass it. Subclassing is especially useful when you want your widget to be reusable.

Misc event handlers

ui.Custom:on_cursor(self) handler Cursor positioning handler.
ui.Custom:on_draw(self) handler Draw handler.
ui.Custom:on_focus(self) handler Focus handler.
ui.Custom:on_hotkey(self, keycode) handler Global keypress handler.
ui.Custom:on_key(self, keycode) handler Keypress handler.
ui.Custom:on_unfocus(self) handler Unfocus handler.

Mouse event handlers

ui.Custom:on_click(self, x, y, buttons, count) handler Mouse click handler.
ui.Custom:on_mouse_down(self, x, y, buttons, count) handler Mouse down handler.
ui.Custom:on_mouse_drag(self, x, y, buttons) handler Mouse drag handler.
ui.Custom:on_mouse_move(self, x, y) handler Mouse move handler.
ui.Custom:on_mouse_scroll_down(self, x, y) handler Mouse scroll down handler.
ui.Custom:on_mouse_scroll_up(self, x, y) handler Mouse scroll up handler.
ui.Custom:on_mouse_up(self, x, y, buttons, count) handler Mouse up handler.


Misc event handlers

ui.Custom:on_cursor(self) handler
Cursor positioning handler.

This handler is called to position the cursor. It is only called for widgets that have the focus.

wdg.on_cursor = function()
  wgt:get_canvas():goto_xy(point.x, point.y)
end

-- To make our widget focusable, we must also do:
wdg.on_focus = function() return true end

You'll always want to implement this handler for focusable widgets or else the cursor will remain at its last arbitrary position.

ui.Custom:on_draw(self) handler
Draw handler.

This is where you draw the contents of your widget. Typically you fetch a canvas object and use its drawing methods:

wdg.on_draw = function(self)
  local c = self:get_canvas()
  c:erase()
  c:draw_string("hi!")
end

See more examples in the page on ui.Canvas.

Right before this handler is called the current style is set to MC’s normal dialog color (appropriate for the active colorset) and the cursor is positioned at the widget’s top-left corner.

ui.Custom:on_focus(self) handler
Focus handler.

Called when a widget is about to receive the focus. You must return true here if you want your widget to receive the focus. Otherwise the widget will be skipped over when the user tries to tab to it.

wdg.on_focus = function()
  return true
end

You will most probably also want to implement on_cursor.

ui.Custom:on_hotkey(self, keycode) handler
Global keypress handler.

This is where you respond to a key pressed when your widget doesn’t necessarily have the focus. (You may alternatively use ui.Dialog:on_key.)

The interface is identical to that of on_key: the handler gets a keycode, and should return true for handled keys.

ui.Custom:on_key(self, keycode) handler
Keypress handler.

This is where you respond to a key pressed when your widget has the focus.

The handler gets as argument the keycode. It should return true if the key was handled.

local K_LEFT = tty.keyname_to_keycode('left')
local K_RIGHT = tty.keyname_to_keycode('right')
...

wgt.on_key = function(self, keycode)
  if keycode == K_LEFT then
    pos.x = pos.x - 1
  elseif keycode == K_RIGHT then
    pos.x = pos.x + 1
  ...
  else
    return false
  end

  self:redraw()
  return true
end

The above bulky code can be made to look more friendly:

local K = utils.magic.memoize(tty.keyname_to_keycode)

wgt.on_key = function(self, keycode)

  if keycode == K'left' then
    pos.x = pos.x - 1
  elseif keycode == K'right' then
    pos.x = pos.x + 1
  elseif keycode == K'up' then
    pos.y = pos.y - 1
  elseif keycode == K'down' then
    pos.y = pos.y + 1
  else
    return false
  end

  self:redraw()
  return true
end

Or you can use a dispatch table:

local K = utils.magic.memoize(tty.keyname_to_keycode)

local navigation = {
  [K'left'] = wgt.go_left,
  [K'right'] = wgt.go_right,
  [K'up'] = wgt.go_up,
  [K'down'] = wgt.go_down,
}

wgt.on_key = function(self, keycode)
  if navigation[keycode] then
    navigation[keycode](self)
    self:redraw()
    return true
  end
end

More examples for handling keys:

If you want to know a key’s name, temporarily turn your key handler into:

wgt.on_key = function(self, k)
  alert(tty.keycode_to_keyname(k))
  return true
end

Note that tty.keycode_to_keyname returns two names. The second —due to the way multiple values are passed around in Lua— will be displayed as the alert’s title.

ui.Custom:on_unfocus(self) handler

Unfocus handler.

Called when a widget is about to lose the focus. If you implement this handler, you must return true here if you want your widget to lose the focus; otherwise the user won’t be able to leave the widget.

If you don’t implement this handler, it’s as if you returned true: the user will be able to always leave the widget.

wdg.on_unfous = function()
  if some_data_is_missing then
    tty.beep()
    return false
  else
    return true
  end
end

Mouse event handlers

For a sample script that uses mouse events, see ui_canvas_mouse.mcs.
ui.Custom:on_click(self, x, y, buttons, count) handler
Mouse click handler.

Called when a mouse button is pressed down and then released inside the widget. According to conventions, this is the desired sequence before taking an action in a UI application. E.g., firing a button’s action or changing a checkbox state should be done in on_click, not in on_mouse_down.

wgt.on_click = function()
  os.execute('firefox')
end

buttons reports the button that was clicked:

wgt.on_click = function(self, x, y, buttons, count)
  if buttons.left and count == 'double' then
    alert(T'You double-clicked the left button.')
  else
end

The system first tries to call “on_mouse_click” and if it’s missing only then it calls “on_click”. This way the widget’s author can reserve “on_click” for end-user supplied actions (this stems from our decision to not use the name "on_action").

ui.Custom:on_mouse_down(self, x, y, buttons, count) handler
Mouse down handler.

Called when a mouse button is pressed down inside the widget.

wgt.on_mouse_down = function()
  wgt:focus()
end

The buttons table reports which buttons are pressed after the event. Valid button names are “left”, “middle”, “right”.

count indicates whether this is part of a double-click or triple-click. It is either “single”, “double”, or “triple”.

This handler is blind to the mouse wheel. If you're interested in the wheel, see on_mouse_scroll_up and on_mouse_scroll_down.

There are a few subtle implementation differences between GPM and xterm:

  • Several buttons may be pressed simultaneously, but when using xterm (as opposed to GPM) only one button will be indicated (in buttons) as pressed.

  • When using GPM, count will show “double” as soon as you press the button (for the second time). When using xterm, however, “double” is indicated a tad later: when you release the button. (However, since you'll be using on_click, as you should, you don’t need to be aware of this issue.)

  • “triple” is supported only on GPM, not xterm.

ui.Custom:on_mouse_drag(self, x, y, buttons) handler
Mouse drag handler.

Called when the mouse pointer, after a mouse button was pressed down inside the widget, is moved (either inside or outside the widget).

local function test2()

  local wgt = ui.Custom{cols=80, rows=5}

  local function draw_point(x, y)
    local c = wgt:get_canvas()
    c:set_style(tty.style('white, red'))
    c:goto_xy(x, y)
    c:draw_string('*')
  end

  wgt.on_mouse_down = function(self, x, y, buttons, count)
    draw_point(x, y)
  end

  wgt.on_mouse_drag = function(self, x, y, ...)
    -- If we remove these checks we'll be able to draw outside the widget.
    if x >= 0 and x < self.cols and y >= 0 and y < self.rows then
      draw_point(x, y)
    end
  end

  ui.Dialog():add(wgt):run()

end

If your drag handler triggers CPU-intensive tasks (these could also be drawing operations), you may benefit greatly from tty.is_idle.

ui.Custom:on_mouse_move(self, x, y) handler
Mouse move handler.

MC doesn’t currently support this event (see here for a start). Only mouse dragging is supported.

ui.Custom:on_mouse_scroll_down(self, x, y) handler

Mouse scroll down handler.

Called when the mouse wheel is rotated towards the user.

wgt.on_mouse_scroll_down = function()
  wgt.top_line = wgt.top_line + 1
  wgt:redraw()
end
ui.Custom:on_mouse_scroll_up(self, x, y) handler

Mouse scroll up handler.

Called when the mouse wheel is rotated away from the user.

wgt.on_mouse_scroll_up = function()
  wgt.top_line = wgt.top_line - 1
  wgt:redraw()
end
ui.Custom:on_mouse_up(self, x, y, buttons, count) handler
Mouse up handler.

Called when a mouse button, that was pressed down inside the widget, is now released (either inside or outside the widget).

buttons reports the button that was released.

generated by LDoc 1.4.3 Last updated 2016-08-23 17:29:40