batframework 1.0.9a11__py3-none-any.whl → 1.0.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. batFramework/__init__.py +53 -76
  2. batFramework/action.py +99 -126
  3. batFramework/actionContainer.py +9 -53
  4. batFramework/animatedSprite.py +114 -56
  5. batFramework/audioManager.py +36 -82
  6. batFramework/camera.py +69 -263
  7. batFramework/constants.py +53 -29
  8. batFramework/cutscene.py +109 -243
  9. batFramework/cutsceneBlocks.py +176 -0
  10. batFramework/debugger.py +48 -0
  11. batFramework/dynamicEntity.py +9 -16
  12. batFramework/easing.py +71 -0
  13. batFramework/entity.py +85 -92
  14. batFramework/gui/__init__.py +3 -14
  15. batFramework/gui/button.py +78 -12
  16. batFramework/gui/constraints.py +204 -0
  17. batFramework/gui/container.py +31 -188
  18. batFramework/gui/debugger.py +43 -126
  19. batFramework/gui/frame.py +19 -0
  20. batFramework/gui/image.py +20 -55
  21. batFramework/gui/indicator.py +22 -95
  22. batFramework/gui/interactiveWidget.py +12 -229
  23. batFramework/gui/label.py +77 -311
  24. batFramework/gui/layout.py +66 -414
  25. batFramework/gui/root.py +35 -203
  26. batFramework/gui/shape.py +57 -247
  27. batFramework/gui/toggle.py +48 -114
  28. batFramework/gui/widget.py +243 -457
  29. batFramework/manager.py +29 -113
  30. batFramework/particles.py +77 -0
  31. batFramework/scene.py +217 -22
  32. batFramework/sceneManager.py +129 -161
  33. batFramework/stateMachine.py +8 -11
  34. batFramework/time.py +75 -0
  35. batFramework/transition.py +124 -129
  36. batFramework/transitionManager.py +0 -0
  37. batFramework/triggerZone.py +4 -4
  38. batFramework/utils.py +144 -266
  39. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/METADATA +24 -17
  40. batframework-1.0.10.dist-info/RECORD +43 -0
  41. batFramework/animation.py +0 -77
  42. batFramework/baseScene.py +0 -240
  43. batFramework/cutsceneManager.py +0 -34
  44. batFramework/drawable.py +0 -77
  45. batFramework/easingController.py +0 -58
  46. batFramework/enums.py +0 -135
  47. batFramework/fontManager.py +0 -65
  48. batFramework/gui/animatedLabel.py +0 -89
  49. batFramework/gui/clickableWidget.py +0 -244
  50. batFramework/gui/constraints/__init__.py +0 -1
  51. batFramework/gui/constraints/constraints.py +0 -980
  52. batFramework/gui/draggableWidget.py +0 -44
  53. batFramework/gui/meter.py +0 -96
  54. batFramework/gui/radioButton.py +0 -35
  55. batFramework/gui/selector.py +0 -250
  56. batFramework/gui/slider.py +0 -397
  57. batFramework/gui/style.py +0 -10
  58. batFramework/gui/styleManager.py +0 -54
  59. batFramework/gui/syncedVar.py +0 -49
  60. batFramework/gui/textInput.py +0 -306
  61. batFramework/gui/tooltip.py +0 -30
  62. batFramework/particle.py +0 -118
  63. batFramework/propertyEaser.py +0 -79
  64. batFramework/renderGroup.py +0 -34
  65. batFramework/resourceManager.py +0 -130
  66. batFramework/sceneLayer.py +0 -138
  67. batFramework/scrollingSprite.py +0 -115
  68. batFramework/sprite.py +0 -51
  69. batFramework/templates/__init__.py +0 -1
  70. batFramework/templates/controller.py +0 -97
  71. batFramework/tileset.py +0 -46
  72. batFramework/timeManager.py +0 -213
  73. batframework-1.0.9a11.dist-info/RECORD +0 -67
  74. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/LICENSE +0 -0
  75. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/WHEEL +0 -0
  76. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/top_level.txt +0 -0
@@ -1,206 +1,49 @@
1
- import pygame
2
1
  import batFramework as bf
3
2
  from .widget import Widget
4
- from .shape import Shape
5
- from .interactiveWidget import InteractiveWidget
6
- from .layout import Layout, Column
3
+ from .layout import Layout
4
+ from .constraints import Constraint
7
5
  from typing import Self
8
- from pygame.math import Vector2
9
6
 
10
-
11
- class Container(Shape, InteractiveWidget):
12
- def __init__(self, layout: Layout = None, *children: Widget) -> None:
7
+ class Container(Widget):
8
+ def __init__(self, layout:Layout=None, *children:Widget):
13
9
  super().__init__()
14
- self.dirty_layout: bool = False
15
10
  self.set_debug_color("green")
16
- self.layout = layout if layout else Column()
17
- self.layout.set_parent(self)
18
- self.scroll = Vector2(0, 0)
19
- self.add(*children)
20
-
21
- def __str__(self) -> str:
22
- return f"Container({self.uid},{len(self.children)})"
23
-
24
- def get_min_required_size(self):
25
- return self.layout.get_auto_size() if self.layout else self.rect.size
26
-
27
- def reset_scroll(self) -> Self:
28
- if self.scroll == (0,0):
29
- return self
30
- self.scroll.update(0, 0)
31
- self.dirty_layout = True
32
- return self
33
-
34
- def set_scroll(self, value: tuple) -> Self:
35
- if (self.scroll.x,self.scroll.y) == value:
36
- return self
37
- self.scroll.update(value)
38
- self.clamp_scroll()
39
- self.dirty_layout = True
40
- return self
41
-
42
- def scrollX_by(self, x: float | int) -> Self:
43
- if x == 0:
44
- return self
45
- self.set_scroll((self.scroll.x + x, self.scroll.y))
46
- return self
11
+ self.surface = None
12
+ self.layout :Layout = layout
13
+ if self.layout : self.layout.set_parent(self)
14
+ for child in children:
15
+ self.add_child(child)
47
16
 
48
- def scrollY_by(self, y: float | int) -> Self:
49
- if y == 0:
50
- return self
51
- self.set_scroll((self.scroll.x, self.scroll.y + y))
52
- return self
53
-
54
- def scroll_by(self, value: tuple[float | int, float | int]) -> Self:
55
- if value[0] == 0 and value[1] == 0:
56
- return self
57
- self.set_scroll((self.scroll.x + value[0], self.scroll.y + value[1]))
58
- return self
59
-
60
- def clamp_scroll(self) -> Self:
61
- if not self.children:
62
- return self
63
- r = self.get_inner_rect()
64
- # Compute the bounding rect of all children in one go
65
- children_rect = self.children[0].rect.copy()
66
- for child in self.children[1:]:
67
- children_rect.union_ip(child.rect)
68
- max_scroll_x = max(0, children_rect.width - r.width)
69
- max_scroll_y = max(0, children_rect.height - r.height)
70
-
71
- # Clamp scroll values only if needed
72
- new_x = min(max(self.scroll.x, 0), max_scroll_x)
73
- new_y = min(max(self.scroll.y, 0), max_scroll_y)
74
-
75
- self.set_scroll((new_x, new_y))
76
- return self
77
-
78
- def set_layout(self, layout: Layout) -> Self:
79
- tmp = self.layout
17
+ def set_layout(self,layout:Layout)->Self:
80
18
  self.layout = layout
81
- if self.layout != tmp:
82
- tmp.set_parent(None)
83
- self.layout.set_parent(self)
84
- self.reset_scroll()
85
- self.dirty_layout = True
19
+ self.apply_constraints()
86
20
  return self
87
21
 
88
- def get_interactive_children(self) -> list[InteractiveWidget]:
89
- return [child for child in self.get_layout_children() if isinstance(child, InteractiveWidget) and not isinstance(child,Container) and child.allow_focus_to_self()]
90
-
91
- def get_layout_children(self)->list[Widget]:
92
- return self.children
22
+ def get_bounding_box(self):
23
+ yield (self.rect,self._debug_color)
24
+ for child in self.children:
25
+ yield from child.get_bounding_box()
93
26
 
94
- def clear_children(self) -> None:
27
+ def clear_children(self)->None:
95
28
  self.children.clear()
96
- self.dirty_layout = True
29
+ self.apply_constraints()
97
30
 
98
- def add(self, *child: Widget) -> Self:
99
- super().add(*child)
100
- self.dirty_shape = True
101
- self.dirty_layout = True
102
- return self
103
-
104
- def remove(self, *child: Widget) -> Self:
105
- super().remove(*child)
106
- self.dirty_shape = True
107
- self.dirty_layout = True
108
- return self
109
-
110
- def top_at(self, x: float | int, y: float | int) -> "None|Widget":
111
- if self.rect.collidepoint(x, y):
112
- for child in reversed(self.children):
113
- result = child.top_at(x, y)
114
- if result is not None:
115
- return result
116
- return self
117
- return None
118
-
119
- def get_focus(self) -> bool:
120
- if not super().get_focus():
121
- return False
122
- interactive_children = self.get_interactive_children()
123
- if not interactive_children:
124
- return True
125
- self.focused_index = min(self.focused_index, len(interactive_children) - 1)
126
- return interactive_children[self.focused_index].get_focus()
31
+ def add_child(self,*child:Widget)->None:
32
+ super().add_child(*child)
33
+ if self.layout : self.layout.arrange()
127
34
 
128
- def children_has_focus(self)->bool:
129
- return any(child.is_focused for child in self.get_interactive_children())
35
+ def remove_child(self,child:Widget)->None:
36
+ super().remove_child(child)
37
+ if self.layout : self.layout.arrange()
130
38
 
131
- def do_handle_event(self, event) -> None:
132
- super().do_handle_event(event)
133
- if event.type == pygame.MOUSEWHEEL:
134
- # Adjust scroll based on the mouse wheel movement
135
- scroll_speed = 20 # Adjust this value to control the scroll speed
136
- self.scroll_by((0, -event.y * scroll_speed)) # Scroll vertically
137
- event.consumed = True # Mark the event as consumed
138
- self.layout.handle_event(event)
139
-
140
- def set_focused_child(self, child: InteractiveWidget) -> bool:
141
- interactive_children = self.get_interactive_children()
142
- try:
143
- index = interactive_children.index(child)
144
- self.focused_index = index
145
- if self.layout :
146
- self.layout.scroll_to_widget(child)
147
- return True
148
- except ValueError:
149
- return False
150
-
151
- def allow_focus_to_self(self) -> bool:
152
- return bool(self.get_interactive_children()) and self.visible
153
-
154
- def build(self) -> None:
155
- if self.layout is not None:
156
- # print("I'm building !",self)
157
- # size = self.expand_rect_with_padding((0,0,*self.layout.get_auto_size())).size
158
- size = self.layout.get_auto_size()
159
- self.set_size(self.resolve_size(size))
39
+ def build(self)->None:
160
40
  super().build()
41
+ if self.layout : self.layout.arrange()
161
42
 
162
- def apply_pre_updates(self):
163
- if self.dirty_size_constraints or self.dirty_shape:
164
- self.resolve_constraints(size_only=True)
165
- self.dirty_size_constraints = False
166
- self.dirty_position_constraints = True
167
-
168
- if self.dirty_layout:
169
- self.layout.update_child_constraints()
170
- self.layout.arrange()
171
- self.dirty_layout = False
172
-
173
- def apply_post_updates(self,skip_draw:bool=False):
174
- """
175
- BOTTOM TO TOP
176
- for cases when widget attributes depend on children attributes
177
- """
178
- if self.dirty_shape:
179
- self.layout.update_child_constraints()
180
- self.build()
181
- self.dirty_shape = False
182
- self.dirty_surface = True
183
- self.dirty_layout = True
184
- self.dirty_size_constraints = True
185
- self.dirty_position_constraints = True
186
- from .container import Container
187
- if self.parent and isinstance(self.parent, Container):
188
- self.parent.dirty_layout = True
189
- self.parent.dirty_shape = True
190
-
191
- # trigger layout or constraint updates in parent
192
-
193
-
194
- # force recheck of constraints
195
-
196
-
197
- if self.dirty_position_constraints:
198
- self.resolve_constraints(position_only=True)
199
- self.dirty_position_constraints= False
200
-
201
-
202
- if self.dirty_surface and not skip_draw:
203
- self.paint()
204
- self.dirty_surface = False
205
-
43
+ def apply_constraints(self)->None:
44
+ super().apply_constraints()
45
+ if self.layout : self.layout.arrange()
206
46
 
47
+ def to_string_id(self)->str:
48
+ return f"Container({len(self.children)},{[c.to_string() for c in self.constraints]})"
49
+
@@ -1,130 +1,47 @@
1
1
  from .label import Label
2
- from typing import Self,Callable,Any
3
- import batFramework as bf
4
- import pygame
5
- import sys
6
-
7
-
8
- def convert_to_int(*args):
9
- return [int(arg) for arg in args]
10
-
2
+ from typing import Self
11
3
 
12
4
  class Debugger(Label):
13
- def __init__(self) -> None:
14
- super().__init__("")
15
- self.static_data: dict[str,Any] = {}
16
- self.dynamic_data: dict[str,Callable[[],str]] = {}
17
- self.refresh_rate = 10
18
- self.refresh_counter: float = 0
19
- self.add_tags("debugger")
20
- self.set_visible(False)
21
-
22
- def set_refresh_rate(self, value: int) -> Self:
23
- self.refresh_rate = value
24
- return self
25
-
26
- def add_static(self, key: str, data):
27
- self.static_data[key] = str(data)
28
- self.update_text()
29
-
30
- def add_dynamic(self, key: str, func:Callable[[],str]) -> None:
31
- self.dynamic_data[key] = func
32
- self.update_text()
33
-
34
- def remove_static(self, key:str) -> bool:
35
- try:
36
- self.static_data.pop(key)
37
- return True
38
- except KeyError:
39
- return False
40
-
41
- def remove_dynamic(self, key:str) -> bool:
42
- try:
43
- self.dynamic_data.pop(key)
44
- return True
45
- except KeyError:
46
- return False
47
-
48
- def set_parent_scene(self, scene) -> Self:
49
- super().set_parent_scene(scene)
50
- self.set_render_order(sys.maxsize-100)
51
- self.update_text()
52
- return self
53
-
54
- def set_text(self, text: str) -> Self:
55
- return super().set_text(text)
56
-
57
- def update_text(self) -> None:
58
- if not self.parent_scene:
59
- return
60
-
61
- d = "\n".join(
62
- key + ":" + data if key != "" else data
63
- for key, data in self.static_data.items()
64
- )
65
-
66
- d2 = "\n".join(
67
- key + ":" + str(data()) if key != "" else str(data())
68
- for key, data in self.dynamic_data.items()
69
- )
70
-
71
- self.set_text("\n".join((d, d2)).strip())
72
-
73
- def update(self, dt: float) -> None:
74
- if not self.parent_scene:
75
- return
76
-
77
- if bf.ResourceManager().get_sharedVar("debug_mode") != bf.debugMode.DEBUGGER:
78
- self.set_visible(False)
79
- return
80
-
81
- self.set_visible(True)
82
- self.refresh_counter = self.refresh_counter + (dt * 60)
83
-
84
- if self.refresh_counter > self.refresh_rate:
85
- self.refresh_counter = 0
86
- self.update_text()
87
-
88
-
89
- def __str__(self) -> str:
90
- return "Debugger"
91
-
92
- def top_at(self, x, y):
93
- return None
94
-
95
-
96
- class FPSDebugger(Debugger):
97
- def __init__(self):
98
- super().__init__()
99
-
100
- def do_when_added(self):
101
- if not self.parent_scene or not self.parent_scene.manager:
102
- print("Debugger could not link to the manager")
103
- return
104
- manager_link = self.parent_scene.manager
105
- self.add_dynamic("FPS", lambda: str(round(manager_link.get_fps())))
106
-
107
-
108
- class BasicDebugger(FPSDebugger):
109
- def do_when_added(self):
110
- if not self.parent_scene or not self.parent_scene.manager:
111
- print("Debugger could not link to the manager")
112
- return
113
- self.add_dynamic(
114
- "Resolution", lambda: "x".join(str(i) for i in bf.const.RESOLUTION)
115
- )
116
- super().do_when_added()
117
- self.add_dynamic("Mouse", pygame.mouse.get_pos)
118
-
119
- if not hasattr(self.parent_scene,"root"):
120
- print("Debugger couldn't find 'root' widget in parent scene")
121
- return
5
+ def __init__(self)->None:
6
+ super().__init__("")
7
+ self.static_data : dict = {}
8
+ self.dynamic_data : dict = {}
9
+ self.refresh_rate = 10
10
+ self.refresh_counter = 0
11
+ self.render_order = 99
12
+
13
+ def to_string_id(self)->str:
14
+ return "Debugger"
15
+
16
+ def set_refresh_rate(self, value:int)-> Self:
17
+ self.refresh_rate = value
18
+ return self
19
+
20
+ def add_data(self,key:str,data):
21
+ self.static_data[key] = str(data)
22
+ self.update_text()
23
+
24
+ def add_dynamic_data(self,key:str,func)->None:
25
+ self.dynamic_data[key] = func
26
+ self.update_text()
27
+
28
+ def set_parent_scene(self,scene)->None:
29
+ super().set_parent_scene(scene)
30
+ self.update_text()
122
31
 
123
- parent_scene = self.parent_scene
124
-
125
- self.add_dynamic(
126
- "Hover",
127
- lambda: (
128
- str(parent_scene.root.hovered) if parent_scene.root.hovered else None
129
- ),
130
- )
32
+ def update_text(self)->None:
33
+ if not self.parent_scene:return
34
+ d = '\n'.join(key+':'+data if key!='' else data for key,data in self.static_data.items())
35
+ d2 = '\n'.join(key+':'+str(data()) if key!='' else str(data()) for key,data in self.dynamic_data.items())
36
+ self.set_text('\n'.join((d,d2)).strip())
37
+
38
+ def update(self,dt:float)->None:
39
+ if not self.parent_scene:return
40
+ if self.parent_scene.get_sharedVar("is_debugging_func")() != 1:
41
+ self.set_visible(False)
42
+ return
43
+ self.set_visible(True)
44
+ self.refresh_counter = self.refresh_counter + (dt * 60)
45
+ if self.refresh_counter > self.refresh_rate:
46
+ self.refresh_counter = 0
47
+ self.update_text()
@@ -0,0 +1,19 @@
1
+ import batFramework as bf
2
+ from .shape import Shape
3
+ import pygame
4
+
5
+
6
+ class Frame(Shape):
7
+ def __init__(self,width:float,height:float):
8
+ super().__init__(width,height)
9
+ self.set_debug_color("magenta")
10
+
11
+ def to_string_id(self)->str:
12
+ return "Frame"
13
+
14
+ def children_modified(self)->None:
15
+ print(self.children)
16
+ self.set_size(
17
+ *self.inflate_rect_by_padding(self.rect.unionall(list(c.rect for c in self.children))).size
18
+ )
19
+ super().children_modified()
batFramework/gui/image.py CHANGED
@@ -1,58 +1,23 @@
1
1
  import batFramework as bf
2
2
  from .widget import Widget
3
- from .shape import Shape
4
3
  import pygame
5
- from typing import Self
6
-
7
-
8
- class Image(Shape):
9
- def __init__(
10
- self,
11
- path: str = None,
12
- convert_alpha=True,
13
- ):
14
- self.original_surface = None
15
- super().__init__(convert_alpha=convert_alpha)
16
- if path is not None:
17
- self.from_path(path)
18
-
19
- def __str__(self) -> str:
20
- return "Image"
21
-
22
- def paint(self) -> None:
23
- super().paint()
24
- if self.original_surface is None:
25
- return
26
- padded = self.get_inner_rect().move(-self.rect.x,-self.rect.y)
27
- target_size = padded.size
28
- if self.original_surface.get_size() != target_size:
29
- self.surface.blit(pygame.transform.scale(self.original_surface, target_size), padded.topleft)
30
- else:
31
- self.surface.blit(self.original_surface, padded.topleft)
32
-
33
- def build(self) -> None:
34
- if self.original_surface is not None:
35
- self.set_size(
36
- self.expand_rect_with_padding((0,0,*self.original_surface.get_size())).size
37
- )
38
- super().build()
39
-
40
-
41
- def from_path(self, path: str) -> Self:
42
- tmp = bf.ResourceManager().get_image(path, self.convert_alpha)
43
- if tmp is None:
44
- return self
45
- self.original_surface = tmp
46
- size = self.original_surface.get_size()
47
- self.set_size(size)
48
- self.dirty_surface = True
49
- return self
50
-
51
- def from_surface(self, surface: pygame.Surface) -> Self:
52
- if surface is None:
53
- return self
54
- self.original_surface = surface
55
- size = self.original_surface.get_size()
56
- self.set_size(size)
57
- self.dirty_surface = True
58
- return self
4
+ class Image(Widget):
5
+ def __init__(self,data:pygame.Surface|str,size:None|tuple[int,int]=None,convert_alpha=True):
6
+ super().__init__(False)
7
+ self.surface = None
8
+ if isinstance(data,str):
9
+ path = bf.utils.get_path(data)
10
+ self.original_surface=pygame.image.load(path)
11
+ elif isinstance(data,pygame.Surface):
12
+ self.original_surface = data
13
+
14
+ if convert_alpha: self.original_surface = self.original_surface.convert_alpha()
15
+ if not size : size = self.original_surface.get_size()
16
+ self.set_size(*size)
17
+
18
+
19
+ def build(self)->None:
20
+ if not self.surface or self.surface.get_size() != self.get_size_int():
21
+ self.surface = pygame.transform.scale(self.original_surface,self.get_size_int())
22
+
23
+
@@ -1,113 +1,40 @@
1
1
  from .shape import Shape
2
- from typing import Any, Self, Callable
2
+ from typing import Any
3
3
  import pygame
4
- from .widget import Widget
5
- from .interactiveWidget import InteractiveWidget
6
- from .draggableWidget import DraggableWidget
7
- import batFramework as bf
8
-
4
+ # from .constraints import ConstraintAspectRatio
9
5
 
10
6
  class Indicator(Shape):
11
- """
12
- Shape intended to be used as icons/indicators
13
- due to its nature, it overrides the top_at function (it can not be 'seen' by the mouse)
14
-
15
- """
16
-
17
- def __init__(self, size: tuple[int | float] = (10, 10)) -> None:
18
- super().__init__(size)
19
- self.debug_color = "magenta"
20
- self.set_outline_width(1)
21
- self.set_outline_color("black")
7
+ def __init__(self,width:int|float= 10,height:int|float=10)->None:
8
+ super().__init__(width,height)
22
9
 
23
- def to_string_id(self) -> str:
10
+ def to_string_id(self)->str:
24
11
  return "Indicator"
25
12
 
26
- def set_value(self, value: Any) -> None:
13
+ def set_value(self,value:Any)->None:
27
14
  pass
28
15
 
29
- def get_value(self) -> Any:
16
+ def get_value(self)->Any:
30
17
  pass
31
18
 
32
- def top_at(self, x, y):
33
- return None
19
+ def _build_indicator(self)->None:
20
+ pass
21
+
22
+ def build(self)->None:
23
+ super().build()
24
+ self._build_indicator()
34
25
 
35
26
 
36
27
  class ToggleIndicator(Indicator):
37
- def __init__(self, default_value: bool) -> None:
38
- super().__init__((20, 20))
39
- self.value: bool = default_value
40
- self.callback = lambda val: self.set_color("green" if val else "red")
41
- self.set_value(default_value)
42
- self.callback(default_value)
43
- # TODO aspect ratio would be good right about here
44
- self.add_constraints(bf.gui.AspectRatio(1,reference_axis=bf.axis.VERTICAL))
45
-
46
- def set_callback(self, callback : Callable[[bool],Any]) -> Self:
47
- self.callback = callback
48
- return self
28
+ def __init__(self,default_value:bool)->None:
29
+ self.value:bool = default_value
30
+ super().__init__(20,20)
31
+ # self.add_constraint(ConstraintAspectRatio(1))
49
32
 
50
- def set_value(self, value: bool) -> None:
33
+ def set_value(self,value)->None:
51
34
  self.value = value
52
- if self.callback:
53
- self.callback(value)
54
- self.dirty_surface = True
35
+ self.set_color("green" if value else "red")
36
+ self.build()
55
37
 
56
- def get_value(self) -> bool:
38
+ def get_value(self)->bool:
57
39
  return self.value
58
-
59
- def top_at(self, x: float, y: float) -> "None|Widget":
60
- r = super().top_at(x, y)
61
- if r is self:
62
- return None
63
- return r
64
-
65
- class ArrowIndicator(Indicator):
66
- def __init__(self,direction:bf.direction):
67
- super().__init__()
68
- self.direction : bf.direction = direction
69
- self.arrow_color = bf.color.WHITE
70
- self.line_width : int = 1
71
-
72
- def set_arrow_color(self,color)-> Self:
73
- self.arrow_color = color
74
- self.dirty_surface = True
75
- return self
76
-
77
- def set_arrow_direction(self,direction:bf.direction)->Self:
78
- self.direction = direction
79
- self.dirty_surface = True
80
- return self
81
-
82
- def set_arrow_line_width(self,value:int)->Self:
83
- self.line_width = value
84
- self.dirty_surface = True
85
- return self
86
-
87
- def paint(self):
88
- super().paint()
89
- r = self.get_local_inner_rect()
90
- size = min(r.width, r.height)
91
- if size %2 == 0:
92
- size -= 1
93
- r.width = size
94
- r.height = size
95
-
96
- #pixel alignment
97
- if (self.padding[1]+self.padding[3] )%2 ==0:
98
- r.height-=1
99
- if (self.padding[0]+self.padding[2] )%2 ==0:
100
- r.width-=1
101
- r.center = self.get_local_inner_rect().center
102
-
103
- bf.utils.draw_triangle(
104
- surface = self.surface,
105
- color = self.arrow_color,
106
- rect =r,
107
- direction = self.direction,
108
- width = self.line_width
109
-
110
- )
111
-
112
-
113
-
40
+ #