HEX support

This commit is contained in:
3alTemp 2024-03-03 20:41:55 -06:00
parent c2d88cab28
commit d8a9a881f4
No known key found for this signature in database
GPG Key ID: FC599A8DFAEC706C
2 changed files with 158 additions and 9 deletions

View File

@ -36,6 +36,7 @@
EraOSBeta!)
- Added a UI for customizing Series Length in Teams and Points-to-Win in FFA
(Thanks EraOSBeta!)
- Implemented HEX code support to the advanced color picker (Thanks 3alTemp!)
### 1.7.32 (build 21741, api 8, 2023-12-20)
- Fixed a screen message that no one will ever see (Thanks vishal332008?...)

View File

@ -43,9 +43,7 @@ class ColorPicker(PopupWindow):
scale = (
2.3
if uiscale is bui.UIScale.SMALL
else 1.65
if uiscale is bui.UIScale.MEDIUM
else 1.23
else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23
)
self._parent = parent
self._position = position
@ -206,9 +204,7 @@ class ColorPickerExact(PopupWindow):
scale = (
2.3
if uiscale is bui.UIScale.SMALL
else 1.65
if uiscale is bui.UIScale.MEDIUM
else 1.23
else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23
)
self._delegate = delegate
self._transitioning_out = False
@ -217,6 +213,8 @@ class ColorPickerExact(PopupWindow):
self._last_press_time = bui.apptime()
self._last_press_color_name: str | None = None
self._last_press_increasing: bool | None = None
self._hex_timer: bui.AppTimer | None = None
self._hex_prev_text: str = '#FFFFFF'
self._change_speed = 1.0
width = 180.0
height = 240.0
@ -233,11 +231,25 @@ class ColorPickerExact(PopupWindow):
)
self._swatch = bui.imagewidget(
parent=self.root_widget,
position=(width * 0.5 - 50, height - 70),
size=(100, 70),
texture=bui.gettexture('buttonSquare'),
position=(width * 0.5 - 65 + 5, height - 95),
size=(130, 115),
texture=bui.gettexture('clayStroke'),
color=(1, 0, 0),
)
self._hex_textbox = bui.textwidget(
parent=self.root_widget,
position=(width * 0.5 - 37.5 + 3, height - 51),
max_chars=9,
text='#FFFFFF',
# on_return_press_call=self._done,
autoselect=True,
size=(75, 30),
v_align='center',
editable=True,
maxwidth=70,
force_internal_editing=True,
)
x = 50
y = height - 90
self._label_r: bui.Widget
@ -292,6 +304,33 @@ class ColorPickerExact(PopupWindow):
# color to the delegate, so start doing that.
self._update_for_color()
# Update our HEX stuff!
self._update_for_hex()
self._hex_timer = bui.AppTimer(0.025, self._update_for_hex, repeat=True)
def _update_for_hex(self) -> None:
"""Update for any HEX or color change."""
from typing import cast
hextext = cast(str, bui.textwidget(query=self._hex_textbox))
hexcolor: tuple[float, float, float, float]
# Check if our current hex text doesn't match with our old one.
# Convert our current hex text into a color if possible.
if hextext != self._hex_prev_text:
try:
hexcolor = hex_to_color(hextext)
r, g, b, _ = hexcolor
# Replace the color!
for i, ch in enumerate((r, g, b)):
self._color[i] = max(0.0, min(1.0, ch))
self._update_for_color()
# Usually, a ValueError will occur if the provided hex
# is incomplete, which occurs when in the midst of typing it.
except ValueError:
pass
# Store the current text for our next comparison.
self._hex_prev_text = hextext
# noinspection PyUnresolvedReferences
def _update_for_color(self) -> None:
if not self.root_widget:
@ -307,6 +346,16 @@ class ColorPickerExact(PopupWindow):
if self._delegate is not None:
self._delegate.color_picker_selected_color(self, self._color)
# Show the HEX code of this color.
r, g, b = self._color
hexcode = color_to_hex(r, g, b, None)
self._hex_prev_text = hexcode
bui.textwidget(
edit=self._hex_textbox,
text=hexcode,
color=color_overlay_func(r, g, b),
)
def _color_change_press(self, color_name: str, increasing: bool) -> None:
# If we get rapid-fire presses, eventually start moving faster.
current_time = bui.apptime()
@ -335,6 +384,8 @@ class ColorPickerExact(PopupWindow):
return self._tag
def _transition_out(self) -> None:
# Kill our timer
self._hex_timer = None
if not self._transitioning_out:
self._transitioning_out = True
if self._delegate is not None:
@ -346,3 +397,100 @@ class ColorPickerExact(PopupWindow):
if not self._transitioning_out:
bui.getsound('swish').play()
self._transition_out()
def hex_to_color(hex_color: str) -> tuple:
"""Transforms an RGB / RGBA hex code into an rgb1/rgba1 tuple.
Args:
hex_color (str): The HEX color.
Raises:
ValueError: If the provided HEX color isn't 6 or 8 characters long.
Returns:
tuple: The color tuple divided by 255.
"""
# Remove the '#' from the string if provided.
if hex_color.startswith('#'):
hex_color = hex_color.lstrip('#')
# Check if this has a valid length.
hexlength = len(hex_color)
if not hexlength in [6, 8]:
raise ValueError(f'Invalid HEX color provided: "{hex_color}"')
# Convert the hex bytes to their true byte form.
ar, ag, ab, aa = (
(int.from_bytes(bytes.fromhex(hex_color[0:2]))),
(int.from_bytes(bytes.fromhex(hex_color[2:4]))),
(int.from_bytes(bytes.fromhex(hex_color[4:6]))),
(
int.from_bytes(bytes.fromhex(hex_color[6:8]))
if hexlength == 8
else 255
),
)
# Divide all numbers by 255 and return.
nr, ng, nb, na = (x / 255 if x is not None else None for x in (ar, ag, ab, aa))
return (nr, ng, nb, na) if aa is not None else (nr, ng, nb)
def color_to_hex(r: float, g: float, b: float, a: float | None = 1.0) -> str:
"""Converts an rgb1 tuple to a HEX color code.
Args:
r (float): Red.
g (float): Green.
b (float): Blue.
a (float, optional): Alpha. Defaults to 1.0.
Returns:
str: The hexified rgba values.
"""
# Turn our rgb1 to rgb255
nr, ng, nb, na = [
int(min(255, x * 255)) if x is not None else x for x in [r, g, b, a]
]
# Merge all values into their HEX representation.
hex_code = (
f'#{nr:02x}{ng:02x}{nb:02x}{na:02x}'
if na is not None
else f'#{nr:02x}{ng:02x}{nb:02x}'
)
return hex_code
def color_overlay_func(
r: float, g: float, b: float, a: float | None = None
) -> tuple:
"""I could NOT come up with a better function name.
Args:
r (float): Red.
g (float): Green.
b (float): Blue.
a (float | None, optional): Alpha. Defaults to None.
Returns:
tuple: A brighter color if the provided one is dark,
and a darker one if it's darker.
"""
# Calculate the relative luminance using the formula for sRGB
# https://www.w3.org/TR/WCAG20/#relativeluminancedef
def relative_luminance(color: float) -> Any:
if color <= 0.03928:
return color / 12.92
return ((color + 0.055) / 1.055) ** 2.4
luminance = (
0.2126 * relative_luminance(r)
+ 0.7152 * relative_luminance(g)
+ 0.0722 * relative_luminance(b)
)
# Set our color multiplier depending on the provided color's luminance.
luminant = 1.65 if luminance < 0.33 else 0.2
# Multiply our given numbers, making sure
# they don't blend in the original bg.
avg = (0.7 - (r + g + b / 3)) + 0.15
r, g, b = [max(avg, x * luminant) for x in (r, g, b)]
# Include our alpha and ship it!
return (r, g, b, a) if a is not None else (r, g, b)