batframework 1.0.8a13__py3-none-any.whl → 1.0.9a1__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 (42) hide show
  1. batFramework/__init__.py +11 -15
  2. batFramework/animatedSprite.py +1 -1
  3. batFramework/camera.py +1 -1
  4. batFramework/character.py +1 -1
  5. batFramework/constants.py +10 -0
  6. batFramework/cutscene.py +10 -10
  7. batFramework/drawable.py +75 -0
  8. batFramework/dynamicEntity.py +2 -4
  9. batFramework/entity.py +93 -56
  10. batFramework/enums.py +1 -0
  11. batFramework/fontManager.py +3 -3
  12. batFramework/gui/__init__.py +2 -2
  13. batFramework/gui/{dialogueBox.py → animatedLabel.py} +18 -36
  14. batFramework/gui/button.py +30 -0
  15. batFramework/gui/clickableWidget.py +6 -1
  16. batFramework/gui/constraints/constraints.py +90 -1
  17. batFramework/gui/container.py +84 -93
  18. batFramework/gui/debugger.py +1 -1
  19. batFramework/gui/indicator.py +3 -2
  20. batFramework/gui/interactiveWidget.py +43 -24
  21. batFramework/gui/label.py +43 -49
  22. batFramework/gui/layout.py +378 -42
  23. batFramework/gui/root.py +2 -9
  24. batFramework/gui/shape.py +2 -0
  25. batFramework/gui/textInput.py +115 -78
  26. batFramework/gui/toggle.py +1 -4
  27. batFramework/gui/widget.py +50 -38
  28. batFramework/manager.py +65 -53
  29. batFramework/particle.py +1 -1
  30. batFramework/scene.py +1 -34
  31. batFramework/sceneManager.py +6 -34
  32. batFramework/scrollingSprite.py +1 -1
  33. batFramework/sprite.py +2 -2
  34. batFramework/{time.py → timeManager.py} +0 -2
  35. batFramework/utils.py +118 -19
  36. {batframework-1.0.8a13.dist-info → batframework-1.0.9a1.dist-info}/METADATA +1 -1
  37. batframework-1.0.9a1.dist-info/RECORD +63 -0
  38. {batframework-1.0.8a13.dist-info → batframework-1.0.9a1.dist-info}/WHEEL +1 -1
  39. batFramework/object.py +0 -123
  40. batframework-1.0.8a13.dist-info/RECORD +0 -63
  41. {batframework-1.0.8a13.dist-info → batframework-1.0.9a1.dist-info}/LICENCE +0 -0
  42. {batframework-1.0.8a13.dist-info → batframework-1.0.9a1.dist-info}/top_level.txt +0 -0
batFramework/__init__.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import pygame
2
2
  import batFramework as bf
3
- import sys
4
3
  from .constants import Constants as const
5
4
  from .utils import Singleton
6
5
  from .enums import *
@@ -8,17 +7,17 @@ from .resourceManager import ResourceManager
8
7
  from .fontManager import FontManager
9
8
  from .utils import Utils as utils
10
9
  from .tileset import Tileset
11
- from .time import *
10
+ from .timeManager import TimeManager,Timer,SceneTimer
12
11
  from .easingController import EasingController
13
12
  from .cutscene import Cutscene, CutsceneManager
14
- from .cutsceneBlocks import *
13
+ import batFramework.cutsceneBlocks
15
14
  from .audioManager import AudioManager
16
15
  import batFramework.transition as transition
17
16
  from .action import Action
18
17
  from .actionContainer import *
19
18
  from .camera import Camera
20
- from .object import Object
21
19
  from .entity import Entity
20
+ from .drawable import Drawable
22
21
  from .renderGroup import RenderGroup
23
22
  from .dynamicEntity import DynamicEntity
24
23
  from .sprite import Sprite
@@ -29,7 +28,7 @@ from .animatedSprite import AnimatedSprite
29
28
  from .character import Character
30
29
  from .stateMachine import State, StateMachine
31
30
  from .scene import Scene
32
- from .gui import *
31
+ from .gui import *
33
32
  from .sceneManager import SceneManager
34
33
  from .manager import Manager
35
34
  from .templates import *
@@ -47,8 +46,6 @@ def init_screen(resolution: tuple[int, int], flags: int = 0, vsync: int = 0):
47
46
  f"Window : {resolution[0]}x{resolution[1]}"
48
47
  )
49
48
 
50
-
51
-
52
49
  def print_version():
53
50
  package_name = "batFramework"
54
51
  try:
@@ -57,19 +54,18 @@ def print_version():
57
54
  except importlib.metadata.PackageNotFoundError:
58
55
  print(f"{package_name} is not installed")
59
56
 
60
-
61
57
  def init(
62
58
  resolution: tuple[int, int],
63
59
  flags: int = 0,
64
- vsync: int = 0,
65
- default_text_size=None,
66
- default_font=None,
60
+ window_caption: str = "BatFramework Project",
67
61
  resource_path: str | None = None,
68
- window_title: str = "BatFramework Project",
62
+ default_font_size=None,
63
+ default_font=None,
69
64
  fps_limit: int = 0,
65
+ vsync: int = 0,
70
66
  ):
71
67
  print_version()
72
- pygame.display.set_caption(window_title)
68
+ pygame.display.set_caption(window_caption)
73
69
  init_screen(resolution, flags, vsync)
74
70
 
75
71
  ResourceManager().set_resource_path(
@@ -77,8 +73,8 @@ def init(
77
73
  )
78
74
  if resource_path is not None:
79
75
  ResourceManager().load_dir(ResourceManager().RESOURCE_PATH)
80
- if default_text_size is not None:
81
- FontManager().set_default_text_size(default_text_size)
76
+ if default_font_size is not None:
77
+ FontManager().set_default_text_size(default_font_size)
82
78
  FontManager().init_font(default_font)
83
79
  const.BF_INITIALIZED = True
84
80
  const.set_fps_limit(fps_limit)
@@ -3,7 +3,7 @@ import pygame
3
3
  from typing import List, Dict, Tuple, Union, Optional, Self
4
4
  from .animation import Animation
5
5
 
6
- class AnimatedSprite(bf.Entity):
6
+ class AnimatedSprite(bf.Drawable):
7
7
  def __init__(self, size: Optional[Tuple[int, int]] = None,*args,**kwargs) -> None:
8
8
  super().__init__(size, no_surface=True,*args,**kwargs)
9
9
  self.float_counter: float = 0
batFramework/camera.py CHANGED
@@ -213,7 +213,7 @@ class Camera:
213
213
 
214
214
  return surface
215
215
 
216
- def resize(self, size: tuple[int, int] | None = None) -> Self:
216
+ def set_size(self, size: tuple[int, int] | None = None) -> Self:
217
217
  if size is None:
218
218
  size = bf.const.RESOLUTION
219
219
  self.target_size = size
batFramework/character.py CHANGED
@@ -3,7 +3,7 @@ from .stateMachine import State
3
3
  from .animatedSprite import AnimatedSprite
4
4
  from .dynamicEntity import DynamicEntity
5
5
 
6
- class Character(AnimatedSprite,DynamicEntity):
6
+ class Character(DynamicEntity,AnimatedSprite):
7
7
  def __init__(self,*args,**kwargs) -> None:
8
8
  super().__init__(*args,**kwargs)
9
9
  self.state_machine = bf.StateMachine(self)
batFramework/constants.py CHANGED
@@ -4,6 +4,8 @@ import pygame
4
4
  class Constants:
5
5
  SCREEN: pygame.Surface = None
6
6
  RESOLUTION: tuple[int, int] = (1280, 720)
7
+ WIDTH = 1280
8
+ HEIGHT = 720
7
9
  VSYNC = 0
8
10
  FLAGS: int = pygame.SCALED | pygame.RESIZABLE
9
11
  FPS: int = 60
@@ -14,10 +16,18 @@ class Constants:
14
16
  DEFAULT_CLICK_CURSOR = pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW)
15
17
 
16
18
  BF_INITIALIZED: bool = False
19
+ ALLOW_DEBUG : bool = True
20
+
21
+ @staticmethod
22
+ def set_allow_debug(allow_debug:bool):
23
+ Constants.ALLOW_DEBUG = allow_debug
24
+
17
25
 
18
26
  @staticmethod
19
27
  def set_resolution(resolution: tuple[int, int]):
20
28
  Constants.RESOLUTION = resolution
29
+ Constants.WIDTH = resolution[0]
30
+ Constants.HEIGHT = resolution[1]
21
31
 
22
32
  @staticmethod
23
33
  def set_default_cursor(cursor: pygame.Cursor):
batFramework/cutscene.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import batFramework as bf
2
- from typing import TYPE_CHECKING
2
+ from typing import TYPE_CHECKING,Self
3
3
 
4
4
  class CutsceneBlock: ...
5
5
 
@@ -37,14 +37,13 @@ class CutsceneManager(metaclass=bf.Singleton):
37
37
  self.current_cutscene.on_enter()
38
38
  self.current_cutscene.init_blocks()
39
39
  self.current_cutscene.play()
40
- self.manager.set_sharedVar("in_cutscene", True)
41
40
 
42
41
  def enable_player_control(self) -> None:
43
- self.manager.set_sharedVar("player_has_control", True)
42
+ pass
44
43
 
45
44
  def disable_player_control(self) -> None:
46
- self.manager.set_sharedVar("player_has_control", False)
47
-
45
+ pass
46
+
48
47
  def update(self, dt):
49
48
  if not self.current_cutscene is None:
50
49
  self.current_cutscene.update(dt)
@@ -56,7 +55,6 @@ class CutsceneManager(metaclass=bf.Singleton):
56
55
  self.play(self.cutscenes.pop(0))
57
56
  else:
58
57
  self.current_cutscene = None
59
- self.manager.set_sharedVar("in_cutscene", False)
60
58
 
61
59
 
62
60
  class Cutscene:
@@ -75,13 +73,15 @@ class Cutscene:
75
73
  def init_blocks(self):
76
74
  pass
77
75
 
78
- def add_blocks(self, *blocks: CutsceneBlock):
76
+ def add_blocks(self, *blocks: CutsceneBlock)->Self:
79
77
  self.cutscene_blocks.extend(blocks)
80
-
81
- def add_end_blocks(self, *blocks: CutsceneBlock):
78
+ return self
79
+
80
+ def add_end_blocks(self, *blocks: CutsceneBlock)->Self:
82
81
  _ = [block.set_parent_cutscene(self) for block in blocks]
83
82
  self.end_blocks.extend(blocks)
84
-
83
+ return self
84
+
85
85
  def get_scene_at(self, index):
86
86
  return bf.CutsceneManager().manager.scenes[index]
87
87
 
@@ -0,0 +1,75 @@
1
+ from typing import Any, Self
2
+ import pygame
3
+ import batFramework as bf
4
+ from .entity import Entity
5
+
6
+
7
+ class Drawable(Entity):
8
+ """
9
+ Basic entity class
10
+ """
11
+
12
+ def __init__(
13
+ self,
14
+ size: None | tuple[int, int] = None,
15
+ surface_flags: int = 0,
16
+ convert_alpha: bool = False,
17
+ *args,
18
+ **kwargs,
19
+ ) -> None:
20
+ super().__init__()
21
+ self.visible: bool = True
22
+ self.render_order: int = 0
23
+ self.rect.size = (10, 10) if size is None else size
24
+ self.convert_alpha: bool = convert_alpha
25
+ self.surface_flags: int = surface_flags
26
+ self.blit_flags: int = 0
27
+ self.surface: pygame.Surface = pygame.Surface(self.rect.size, surface_flags)
28
+ if convert_alpha:
29
+ self.surface = self.surface.convert_alpha()
30
+ self.surface.fill((0, 0, 0, 0))
31
+
32
+ def set_alpha(self, alpha: int) -> Self:
33
+ self.surface.set_alpha(min(max(0, alpha), 255))
34
+ return self
35
+
36
+ def get_alpha(self) -> int:
37
+ return self.surface.get_alpha()
38
+
39
+ def set_surface_flags(self, surface_flags: int) -> Self:
40
+ self.surface_flags = surface_flags
41
+ return self
42
+
43
+ def set_convert_alpha(self, value: bool) -> Self:
44
+ self.convert_alpha = value
45
+ return self
46
+
47
+ def set_blit_flags(self, blit_flags: int) -> Self:
48
+ self.blit_flags = blit_flags
49
+ return self
50
+
51
+ def get_debug_outlines(self):
52
+ if self.visible:
53
+ yield (self.rect, self.debug_color)
54
+
55
+ def set_render_order(self, render_order: int) -> Self:
56
+ self.render_order = render_order
57
+ if self.parent_scene:
58
+ self.parent_scene.sort_entities()
59
+ return self
60
+
61
+ def set_visible(self, value: bool) -> Self:
62
+ self.visible = value
63
+ return self
64
+
65
+ def draw(self, camera: bf.Camera) -> None:
66
+ """
67
+ Draw the entity onto the camera surface
68
+ """
69
+ if not self.visible or not camera.rect.colliderect(self.rect) or self.surface.get_alpha() == 0:
70
+ return
71
+ camera.surface.blit(
72
+ self.surface,
73
+ camera.world_to_screen(self.rect),
74
+ special_flags=self.blit_flags,
75
+ )
@@ -6,11 +6,9 @@ from typing import Self
6
6
  class DynamicEntity(bf.Entity):
7
7
  def __init__(
8
8
  self,
9
- size: None | tuple[int, int] = None,
10
- surface_flags: int = 0,
11
- convert_alpha: bool = False,*args,**kwargs
9
+ *args,**kwargs
12
10
  ) -> None:
13
- super().__init__(size, surface_flags, convert_alpha,*args,**kwargs)
11
+ super().__init__(*args,**kwargs)
14
12
  self.velocity = pygame.math.Vector2(0, 0)
15
13
  self.ignore_collisions : bool = False
16
14
 
batFramework/entity.py CHANGED
@@ -1,74 +1,111 @@
1
1
  from typing import Any, Self
2
2
  import pygame
3
3
  import batFramework as bf
4
- from .object import Object
5
-
6
-
7
- class Entity(Object):
8
- """
9
- Basic entity class
10
- """
11
-
12
- def __init__(
13
- self,
14
- size: None | tuple[int, int] = None,
15
- surface_flags: int = 0,
16
- convert_alpha: bool = False,
17
- *args,
18
- **kwargs,
19
- ) -> None:
20
- super().__init__()
21
- self.visible: bool = True
22
- self.rect.size = (10, 10) if size is None else size
23
- self.convert_alpha: bool = convert_alpha
24
- self.surface_flags: int = surface_flags
25
- self.blit_flags: int = 0
26
- self.surface: pygame.Surface = pygame.Surface(self.rect.size, surface_flags)
27
- if convert_alpha:
28
- self.surface = self.surface.convert_alpha()
29
- self.surface.fill((0, 0, 0, 0))
30
-
31
- def set_alpha(self, alpha: int) -> Self:
32
- self.surface.set_alpha(min(max(0, alpha), 255))
33
- return self
4
+ from typing import TYPE_CHECKING
34
5
 
35
- def get_alpha(self) -> int:
36
- return self.surface.get_alpha()
6
+ if TYPE_CHECKING:
7
+ from .camera import Camera
8
+
9
+
10
+ class Entity:
11
+ _count: int = 0
12
+ _available_uids: set[int] = set()
37
13
 
38
- def set_surface_flags(self, surface_flags: int) -> Self:
39
- self.surface_flags = surface_flags
40
- return self
41
14
 
42
- def set_convert_alpha(self, value: bool) -> Self:
43
- self.convert_alpha = value
15
+ def __init__(self,*args,**kwargs) -> None:
16
+ if Entity._available_uids:
17
+ self.uid = Entity._available_uids.pop()
18
+ else:
19
+ self.uid = Entity._count
20
+ Entity._count += 1
21
+
22
+ self.rect = pygame.FRect(0, 0, 0, 0)
23
+ self.tags: list[str] = []
24
+ self.parent_scene: bf.Scene | None = None
25
+ self.debug_color: tuple | str = "red"
26
+
27
+ def __del__(self):
28
+ try:
29
+ Entity._available_uids.add(self.uid)
30
+ except AttributeError:
31
+ pass
32
+ def set_position(self, x, y) -> Self:
33
+ self.rect.topleft = x, y
44
34
  return self
45
35
 
46
- def set_blit_flags(self, blit_flags: int) -> Self:
47
- self.blit_flags = blit_flags
36
+ def set_center(self, x, y) -> Self:
37
+ self.rect.center = x, y
48
38
  return self
49
39
 
50
40
  def get_debug_outlines(self):
51
- if self.visible:
52
- yield (self.rect, self.debug_color)
41
+ yield (self.rect, self.debug_color)
42
+
43
+ def set_debug_color(self, color) -> Self:
44
+ self.debug_color = color
45
+ return self
53
46
 
54
- def set_render_order(self, render_order: int) -> Self:
55
- self.render_order = render_order
56
- if self.parent_scene:
57
- self.parent_scene.sort_entities()
47
+ def set_parent_scene(self, scene) -> Self:
48
+ if scene == self.parent_scene:
49
+ return self
50
+ if self.parent_scene is not None:
51
+ self.do_when_removed()
52
+ self.parent_scene = scene
53
+ if scene is not None:
54
+ self.do_when_added()
58
55
  return self
59
56
 
60
- def set_visible(self, value: bool) -> Self:
61
- self.visible = value
57
+ def do_when_added(self):
58
+ pass
59
+
60
+ def do_when_removed(self):
61
+ pass
62
+
63
+ def add_tags(self, *tags) -> Self:
64
+ for tag in tags:
65
+ if tag not in self.tags:
66
+ self.tags.append(tag)
67
+ self.tags.sort()
62
68
  return self
63
69
 
64
- def draw(self, camera: bf.Camera) -> None:
70
+ def remove_tags(self, *tags):
71
+ self.tags = [tag for tag in self.tags if tag not in tags]
72
+
73
+ def has_tags(self, *tags) -> bool:
74
+ return all(tag in self.tags for tag in tags)
75
+
76
+ def get_tags(self) -> list[str]:
77
+ return self.tags
78
+
79
+ def process_event(self, event: pygame.Event) -> None:
80
+ if event.consumed:
81
+ return
82
+ self.do_process_actions(event)
83
+ self.do_handle_event(event)
84
+
85
+ def do_process_actions(self, event: pygame.Event) -> None:
65
86
  """
66
- Draw the entity onto the camera surface
87
+ Process entity actions you may have set
88
+ """
89
+
90
+ def do_reset_actions(self) -> None:
91
+ """
92
+ Reset entity actions you may have set
93
+ """
94
+
95
+ def do_handle_event(self, event: pygame.Event):
96
+ """
97
+ Handle specific events with no action support
98
+ """
99
+ return False
100
+
101
+ def update(self, dt: float) -> None:
102
+ """
103
+ Update method to be overriden by subclasses of Entity (must call do_update and do_reset_actions)
104
+ """
105
+ self.do_update(dt)
106
+ self.do_reset_actions()
107
+
108
+ def do_update(self, dt: float) -> None:
109
+ """
110
+ Update method to be overriden for specific behavior by the end user
67
111
  """
68
- if not self.visible or not camera.rect.colliderect(self.rect):
69
- return
70
- camera.surface.blit(
71
- self.surface,
72
- camera.world_to_screen(self.rect),
73
- special_flags=self.blit_flags,
74
- )
batFramework/enums.py CHANGED
@@ -111,3 +111,4 @@ class textMode(Enum):
111
111
  ALPHABETICAL = 0
112
112
  NUMERICAL = 1
113
113
  ALPHANUMERICAL = 3
114
+
@@ -9,17 +9,17 @@ import batFramework as bf
9
9
  class FontManager(metaclass=Singleton):
10
10
  def __init__(self):
11
11
  pygame.font.init()
12
- self.DEFAULT_TEXT_SIZE = 16
12
+ self.DEFAULT_FONT_SIZE = 16
13
13
  self.MIN_FONT_SIZE = 8
14
14
  self.MAX_FONT_SIZE = 64
15
15
  self.DEFAULT_ANTIALIAS = False
16
16
  self.FONTS = {}
17
17
 
18
- def set_antialias(self, value: bool):
18
+ def set_default_antialias(self, value: bool):
19
19
  self.DEFAULT_ANTIALIAS = value
20
20
 
21
21
  def set_default_text_size(self, size: int):
22
- self.DEFAULT_TEXT_SIZE = size
22
+ self.DEFAULT_FONT_SIZE = size
23
23
 
24
24
  def init_font(self, raw_path: str | None):
25
25
  try:
@@ -1,4 +1,3 @@
1
- from .constraints import *
2
1
  from .widget import Widget
3
2
  from .styleManager import StyleManager
4
3
  from .style import Style
@@ -10,7 +9,7 @@ from .root import Root
10
9
  from .shape import Shape
11
10
  from .meter import Meter
12
11
  from .label import Label
13
- from .dialogueBox import DialogueBox
12
+ from .animatedLabel import AnimatedLabel
14
13
  from .textInput import TextInput
15
14
  from .button import Button
16
15
  from .debugger import *
@@ -20,3 +19,4 @@ from .indicator import *
20
19
  from .toggle import Toggle
21
20
  from .radioButton import RadioButton, RadioVariable
22
21
  from .slider import Slider
22
+ from .constraints import *
@@ -3,19 +3,20 @@ import batFramework as bf
3
3
  from typing import Self
4
4
 
5
5
 
6
- class DialogueBox(Label):
7
- def __init__(self) -> None:
6
+ class AnimatedLabel(Label):
7
+ def __init__(self,text="") -> None:
8
8
  self.cursor_position: float = 0.0
9
9
  self.text_speed: float = 20.0
10
- self.message_queue: list[str] = []
11
10
  self.is_over: bool = True
12
11
  self.is_paused: bool = False
12
+ self.original_text = ""
13
13
  self.set_autoresize(False)
14
14
  self.set_alignment(bf.alignment.LEFT)
15
15
  super().__init__("")
16
+ self.set_text(text)
16
17
 
17
18
  def __str__(self) -> str:
18
- return "DialogueBox"
19
+ return "AnimatedLabel"
19
20
 
20
21
  def pause(self) -> Self:
21
22
  self.is_paused = True
@@ -53,47 +54,28 @@ class DialogueBox(Label):
53
54
  left = index
54
55
  return [text]
55
56
 
56
- def paint(self) -> None:
57
- if self.font_object and self.message_queue:
58
- message = self.message_queue.pop(0)
59
- message = "\n".join(self.cut_text_to_width(message))
60
- self.message_queue.insert(0, message)
61
- super().paint()
62
57
 
63
- def say(self, message: str) -> Self:
64
- self.message_queue.append(message)
65
- self.is_over = False
58
+ def _set_text_internal(self,text:str)->Self:
59
+ super().set_text(text)
66
60
  return self
67
61
 
68
- def is_queue_empty(self) -> bool:
69
- return not self.message_queue
70
-
71
- def is_current_message_over(self) -> bool:
72
- return self.is_over
73
-
74
- def clear_queue(self) -> Self:
75
- self.message_queue.clear()
76
- self.next_message()
77
- return self
78
-
79
- def next_message(self) -> Self:
80
- if self.message_queue:
81
- self.message_queue.pop(0)
62
+ def set_text(self,text:str)->Self:
63
+ self.original_text = text
64
+ self.is_over = False
82
65
  self.cursor_position = 0
83
- self.set_text("")
84
- return self
85
66
 
86
- def skip_current_message(self) -> Self:
87
- self.cursor_position = len(self.message_queue[0])
88
- self.dirty_shape = True
67
+ def set_size(self, size):
68
+ super().set_size(size)
69
+ self._set_text_internal('\n'.join(self.cut_text_to_width(self.original_text[: int(self.cursor_position)])))
89
70
 
90
71
  def do_update(self, dt):
91
- if not self.message_queue or self.is_paused:
72
+ if self.is_over:
92
73
  return
93
- if not self.is_over and self.cursor_position == len(self.message_queue[0]):
74
+ if not self.is_over and self.cursor_position == len(self.original_text):
94
75
  self.is_over = True
95
76
  return
96
77
  self.cursor_position = min(
97
- self.cursor_position + self.text_speed * dt, len(self.message_queue[0])
78
+ self.cursor_position + self.text_speed * dt, len(self.original_text)
98
79
  )
99
- self.set_text(self.message_queue[0][: int(self.cursor_position)])
80
+ # self.set_text(self.original_text[: int(self.cursor_position)])
81
+ self._set_text_internal('\n'.join(self.cut_text_to_width(self.original_text[: int(self.cursor_position)])))
@@ -13,3 +13,33 @@ class Button(Label, ClickableWidget):
13
13
 
14
14
  def __str__(self) -> str:
15
15
  return f"Button({self.text})"
16
+
17
+ def get_min_required_size(self) -> tuple[float, float]:
18
+ if not (self.autoresize_w or self.autoresize_h):
19
+ return self.rect.size
20
+ if not self.text_rect:
21
+ self.text_rect.size = self._get_text_rect_required_size()
22
+ res = self.inflate_rect_by_padding((0, 0, *self.text_rect.size)).size
23
+ res = res[0],res[1]+self.unpressed_relief
24
+ return res[0] if self.autoresize_w else self.rect.w, (
25
+ res[1] if self.autoresize_h else self.rect.h
26
+ )
27
+
28
+
29
+ def _build_layout(self) -> None:
30
+
31
+ self.text_rect.size = self._get_text_rect_required_size()
32
+ if self.autoresize_h or self.autoresize_w:
33
+ target_rect = self.inflate_rect_by_padding((0, 0, *self.text_rect.size))
34
+ target_rect.h += self.unpressed_relief
35
+ if not self.autoresize_w:
36
+ target_rect.w = self.rect.w
37
+ if not self.autoresize_h:
38
+ target_rect.h = self.rect.h
39
+ if self.rect.size != target_rect.size:
40
+ self.set_size(target_rect.size)
41
+ self.apply_updates()
42
+ return
43
+ offset = self._get_outline_offset() if self.show_text_outline else (0,0)
44
+ padded = self.get_padded_rect().move(-self.rect.x + offset[0], -self.rect.y + offset[1])
45
+ self.align_text(self.text_rect, padded, self.alignment)
@@ -27,6 +27,11 @@ class ClickableWidget(Shape, InteractiveWidget):
27
27
  self.set_debug_color("cyan")
28
28
  self.set_relief(self.unpressed_relief)
29
29
 
30
+ def get_min_required_size(self) -> tuple[float, float]:
31
+ return self.rect.size.inflate(0,self.unpressed_relief)
32
+
33
+
34
+
30
35
  def set_unpressed_relief(self, relief: int) -> Self:
31
36
  if relief == self.unpressed_relief:
32
37
  return self
@@ -217,4 +222,4 @@ class ClickableWidget(Shape, InteractiveWidget):
217
222
  self._paint_disabled()
218
223
  elif self.is_hovered:
219
224
  self._paint_hovered()
220
-
225
+