Source code for GLXCurses.ToolBar
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import curses
# It script it publish under GNU GENERAL PUBLIC LICENSE
# http://www.gnu.org/licenses/gpl-3.0.en.html
# Author: the Galaxie Curses Team, all rights reserved
import GLXCurses
[docs]class ToolBar(GLXCurses.Widget):
def __init__(self):
# Load heritage
GLXCurses.Widget.__init__(self)
# It's a GLXCurse Type
self.glxc_type = "GLXCurses.ToolBar"
self.name = "{0}{1}".format(self.__class__.__name__, self.id)
self.foreground_color_normal = (0, 0, 0)
self.background_color_normal = (255, 255, 255)
self.__labels = None
self.labels = None
# States
self.can_default = True
self.can_focus = True
self.can_prelight = True
self.sensitive = True
self.states_list = None
# Subscription
# Mouse
self.connect("MOUSE_EVENT", ToolBar._handle_mouse_event)
# Keyboard
self.connect("CURSES", ToolBar._handle_key_event)
[docs] def draw(self):
"""
Draw the ToolBar widget
"""
self._check_selected()
self.init_button_positions()
# Clear the entire line
for y_inc in range(0, self.height):
for x_inc in range(0, self.width):
try:
self.subwin.delch(y_inc, x_inc)
self.subwin.insstr(y_inc, x_inc, " ", self.style.curses_color_pair_number(0, 0))
except curses.error: # pragma: no cover
pass
for item_number in range(0, len(self.labels)):
if item_number <= 0:
start = 0
stop = self.get_button_width(0)
else:
start = self.labels[item_number - 1]["end_coord"]
stop = start + self.get_button_width(item_number)
if "text" in self.labels[item_number] and self.labels[item_number]["text"]:
# Background
self.add_string(
y=0,
x=start,
text=" " * int((stop + 1) - start),
color=self.color_prelight,
)
# Num Label
self.add_string(
y=0,
x=start,
text=self.labels[item_number]["id"],
color=self.color_normal | GLXCurses.GLXC.A_REVERSE,
)
# text
self.add_string(
y=0,
x=start + 2,
text=GLXCurses.resize_text(
text=self.labels[item_number]["text"],
max_width=self.get_button_width(item_number) - 2,
),
color=self.style.color(
fg=self.style.attribute_to_rgb(
attribute="dark", state="STATE_NORMAL"
),
bg=self.style.attribute_to_rgb(
attribute="base", state="STATE_PRELIGHT"
),
attributes=True,
),
)
@property
def labels(self):
"""
Get the labels list, it contain items with dictionary with key 'id', 'text', 'end_coord'
:return: The labels list
:rtype: list
"""
return self.__labels
@labels.setter
def labels(self, value=None):
"""
Set the labels list
Each list item must contain the text of a single button.
By example:
['Hello','', '', '', '42'] will create 2 buttons separate by the space require for 4 buttons
Take look on the examples directory
:param value: the button list
:type value: list
:raise TypeError: if ``value`` is not a list
"""
if value is None:
value = []
# Exit as soon of possible
if type(value) != list:
raise TypeError("'labels' must be a list type")
self.__labels = []
for _ in value:
self.__labels.append({})
for i in range(0, len(value)):
self.set_label_text(idx=i, text=value[i])
[docs] def init_button_positions(self):
"""
Calculate positions of buttons; width is never less than 7
Else distribute the extra width in a way that the middle vertical line
(between F5 and F6) aligns with the center of the screen.
The extra width is distributed in this order: F10, F5, F9, F4, ..., F6, F1.
"""
pos = 0
# calculate positions of buttons; width is never less than 7
if self.width < len(self.labels) * 7:
for i in range(0, len(self.labels)):
if pos + 7 <= self.width:
pos += 7
if "end_coord" not in self.labels[i] or self.labels[i][
"end_coord"
] != int(pos):
self.labels[i]["end_coord"] = int(pos)
else:
# Distribute the extra width in a way that the middle vertical line
# (between F5 and F6) aligns with the center of the screen. The extra width
# is distributed in this order: F10, F5, F9, F4, ..., F6, F1.
for i in range(0, int(len(self.labels) / 2)):
pos += int(self.width / len(self.labels))
if int(len(self.labels) / 2) - 1 - i < int(
int(self.width % len(self.labels)) / 2
):
pos += 1
if "end_coord" not in self.labels[i] or self.labels[i][
"end_coord"
] != int(pos):
self.labels[i]["end_coord"] = int(pos)
for i in range(int(len(self.labels) / 2), len(self.labels)):
pos += int(self.width / len(self.labels))
if len(self.labels) - 1 - i < int(
(int(self.width % len(self.labels)) + 1) / 2
):
pos += 1
if "end_coord" not in self.labels[i] or self.labels[i][
"end_coord"
] != int(pos):
self.labels[i]["end_coord"] = int(pos)
[docs] def get_button_width(self, i=None):
"""
return width of one button
:param i: button number it start to 0
:type i: int
:return: width of one button
:rtype: int
:raise TypeError: When ``i`` is not a int type
"""
if type(i) != int:
raise TypeError("'i' must be a int value")
if i == 0:
return self.labels[0]["end_coord"]
return self.labels[i]["end_coord"] - self.labels[i - 1]["end_coord"]
[docs] def get_button_by_x_coord(self, x=None):
"""
Return the button number by it X coordinate
:param x: X coordinate value
:type x: int
:return: the button number
:rtype: int
:raise TypeError: When ``x`` is not a int type
"""
if type(x) != int:
raise TypeError("'x' must be a int value")
for i in range(0, len(self.labels)):
if self.labels[i]["end_coord"] > x:
return i
return -1
[docs] def set_label_text(self, idx=None, text=None):
"""
Set the text to a button
:param idx: The button id it start by 0
:type idx: int
:param text: The text to set to it button
:type text: str
"""
if type(idx) != int:
raise TypeError("'idx' must be a int value")
if type(text) != str:
raise TypeError("'text' must be a str value")
if "text" not in self.labels[idx] or self.labels[idx]["text"] != text:
self.labels[idx]["text"] = text
if "id" not in self.labels[idx] or self.labels[idx]["id"] != "{0: >2}".format(
idx + 1
):
self.labels[idx]["id"] = "{0: >2}".format(idx + 1)
def _check_selected(self):
if self.can_default:
if GLXCurses.Application().has_default:
if GLXCurses.Application().has_default.id == self.id:
self.has_default = True
else:
self.has_default = False
if self.can_focus:
if GLXCurses.Application().has_focus:
if GLXCurses.Application().has_focus.id == self.id:
self.has_focus = True
else:
self.has_focus = False
if self.can_prelight:
if GLXCurses.Application().has_prelight:
if GLXCurses.Application().has_prelight.id == self.id:
self.has_prelight = True
else:
self.has_prelight = False
def _handle_mouse_event(self, event_signal, event_args): # pragma: no cover
if self.sensitive:
(mouse_event_id, x, y, z, event) = event_args
# convert mouse point of view to Area point of view
y -= self.y
x -= self.x
if 0 <= y <= self.height - 1 and 0 <= x <= self.width - 1:
# We are sure about the ToolBar have been clicked
if not self.has_focus or not self.has_default or not self.has_prelight:
self.emit("grab-focus", {"id": self.id})
# if self.can_focus:
# self.emit("CLAIM_FOCUS", {"id": self.id})
# if self.can_default:
# self.emit("CLAIM_DEFAULT", {"id": self.id})
# if self.can_prelight:
# self.emit("RELEASE_PRSELIGHT", {"id": self.id})
if (
event == GLXCurses.GLXC.BUTTON1_CLICKED
or event == GLXCurses.GLXC.BUTTON1_RELEASED
):
self.emit(
"F{0}_CLICKED".format(self.get_button_by_x_coord(x) + 1),
{
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F{0}_CLICKED".format(
self.get_button_by_x_coord(x) + 1
),
},
)
def _handle_key_event(self, event_signal, *event_args): # pragma: no cover
# Check if we have to care about keyboard event
if (
isinstance(GLXCurses.Application().has_focus, GLXCurses.ChildElement)
and GLXCurses.Application().has_focus.id == self.id
):
# Touch Escape
if event_args[0] == GLXCurses.GLXC.KEY_ESC:
self.emit("RELEASE_FOCUS", {"id": self.id})
self.emit("RELEASE_DEFAULT", {"id": self.id})
self.emit("RELEASE_PRELIGHT", {"id": self.id})
if event_args[0] == GLXCurses.GLXC.KEY_F1:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F1_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F2:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F2_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F3:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F3_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F4:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F4_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F5:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F5_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F6:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F6_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F7:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F7_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F8:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F8_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F9:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F9_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
elif event_args[0] == GLXCurses.GLXC.KEY_F10:
instance = {
"class": self.__class__.__name__,
"id": self.id,
"event_signal": "F10_PRESSED",
}
self.emit(str(instance["event_signal"]), instance)
[docs] def update_preferred_sizes(self):
self.preferred_height = 1
self.preferred_width = self.width