11. Formaat inventory wijzigen#

De breedte van de inventory hebben met inventory_width = 300 ingesteld op 300 pixels. We kunnen de speler de mogelijkheid geven om de breedte van de inventory te wijzigen door de muiscursor op de rand tussen de inventory en de workbench te plaatsen en vervolgens te slepen. Hiervoor maken we eerst twee nieuwe variabelen aan:

 8# WINDOW SETTINGS
 9
10WIDTH = 800
11HEIGHT = 450
12TITLE = 'Alchemy'
13
14inventory_width = 300
15inventory_min_width = 300
16inventory_rect = Rect((0, 0), (inventory_width, HEIGHT))
17workbench_rect = Rect((inventory_width, 0), (WIDTH-inventory_width, HEIGHT))
18PADDING = 3
19
20# CARD SETTINGS
21
22ICONSIZE = 32
23LEFTMARGIN = 0
24RIGHTMARGIN = 10
25TOPMARGIN = 3
26BOTTOMMARGIN = 3
27HSPACE = 5
28FONTSIZE = 30
29card_font = pygame.font.SysFont(None, FONTSIZE)
30CARD_HEIGHT = TOPMARGIN + max(ICONSIZE, FONTSIZE) + BOTTOMMARGIN
31
32# DICTIONARIES AND LISTS
33
34elements = {}
35recipes = {}
36workbench = []
37inventory = []
38
39# VARIABLES
40
41resizing = False
42dragging = False
43dragged = {}
44other = -1

De inventory_min_width variabele geeft de minimaal benodigde breedte van de inventory aan. We zetten deze in eerste instantie even op 300 pixels, maar gaan de juiste waarde straks berekenen in de calc_card_rects() functie. De boolean variabele resizing geeft aan of de speler het formaat van de inventory aan het aanpassen is. We zetten deze standaard op False.

In de calc_card_rects() functie berekenen we de juiste waarde van inventory_min_width door de breedte van de grootste card in de inventory te nemen en daar 2 keer de PADDING waarde bij op te tellen.

 8def calc_card_rects():
 9   global inventory_min_width
10   max_card_width = 0
11   for key, value in elements.items():
12      lbl_width, lbl_height = card_font.size(value['label'])
13      rect_width = LEFTMARGIN + ICONSIZE + HSPACE + lbl_width + RIGHTMARGIN
14      rect_height = CARD_HEIGHT
15      r = Rect((0, 0), (rect_width, rect_height))
16      if r.width > (inventory_width - 2 * PADDING):
17            raise Exception(f'Width of element \"{key}\" ({r.width} px) too large for side bar ({inventory_width - 2 * PADDING} px).')
18      elements[key]['rect'] = r
19      max_card_width = max(max_card_width, r.width)
20   inventory_min_width = max_card_width + 2 * PADDING

In regel 10 maken we een lokale variabele max_card_width aan die we gaan gebruiken om de breedte van de grootste card in de inventory te bepalen. In de for loop zetten we op regel 19 de waarde van max_card_width op max(max_card_width, r.width), oftewel de grootste waarde van max_card_width en r.width. Dat betekent: als max_card_width groter is dan de breedte van de huidige card, dan blijft de waarde van max_card_width gelijk. Is de breedte van de huidige card echter groter, dan wordt max_card_width aangepast naar die waarde.
In regels 16 en 17 is een extra check toegevoegd om ervoor te zorgen dat de breedte van de element cards niet te groot is voor de inventory.

Nu moeten we nog de drie mouse event handlers aanpassen om het resizen van de inventory mogelijk te maken. Maar voordat we dat doen, maken we een eenvoudige functie om te checken of de muiscursor op de rand van de inventory staat:

107def mouse_on_resize(pos):
108   return abs(pos[0] - inventory_width) <= 2

Als de absolute waarde van het verschil tussen de x-coördinaat van de muiscursor en de breedte van de inventory kleiner of gelijk is aan 2, dan retourneert de functie mouse_on_resize() True en anders False. Er is dus een marge van 2 pixels aan beide zijden van de rand van de inventory.

Absolute waarde

De absolute waarde van een getal is de waarde zonder teken. Dus de absolute waarde van -5 is 5 en de absolute waarde van 5 is ook 5. In Python kun je de absolute waarde van een getal berekenen met de abs() functie.

In regel 108 gebruiken we deze functie omdat de muis zowel links als rechts van de rand van de inventory kan staan. Als de muis links van de rand staat, is het verschil pos[0] - inventory_width negatief en als de muis rechts van de rand staat, is het verschil positief. Door de absolute waarde te nemen, kunnen we beide gevallen op dezelfde manier behandelen.

In de on_mouse_down() functie moeten we nu checken of de muiscursor op de rand van de inventory staat. Als dat het geval is, zetten we de resizing variabele op True en verlaten meteen de functie:

110def on_mouse_down(pos, button):
111   global dragged, dragging, resizing
112   if mouse_on_resize(pos):
113      resizing = True
114      return
115   if pos[0] < inventory_width:
116      # Clicked in inventory
117      ...

In on_mouse_move() breiden we het if statement als volgt uit:

143def on_mouse_move(pos):
144   global other, inventory_width
145   if dragging:
146      dragged['rect'].x = pos[0] - dragged['click_pos'][0]
147      dragged['rect'].y = pos[1] - dragged['click_pos'][1]
148      for index, card in enumerate(workbench):
149            if dragged['rect'].colliderect(card['rect']):
150               other = index
151               return
152      other = -1
153   elif resizing:
154      if pos[0] > inventory_min_width:
155            inventory_width = pos[0]
156            invalidate_window()
157   else:
158      if mouse_on_resize(pos):
159            pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_SIZEWE)
160      else:
161            pygame.mouse.set_cursor()

Als de speler aan het resizen is, controleren we of de muiscursor zich rechts van de minimale breedte van de inventory bevindt. Als dat zo is, passen we de breedte van de inventory aan naar de x-coördinaat van de muiscursor en roepen we invalidate_window() aan. Deze functie, die we nog moeten maken, zorgt ervoor dat het venster opnieuw wordt getekend.
Als de speler geen element aan het slepen is en ook niet bezig is met resizen, dan controleren we of de muiscursor op de rand van de inventory staat. Als dat zo is, veranderen we de vorm van de muiscursor om de speler te laten zien dat hij de breedte van de inventory kan aanpassen.

Aan de on_mouse_up() functie voegen we de volgende code toe:

163def on_mouse_up():
164   global dragging, other, resizing
165   if resizing:
166      resizing = False
167      for card in workbench.copy():
168            if not workbench_rect.contains(card['rect']):
169               workbench.remove(card)
170      return
171   if dragging:
172      dragging = False
173      ...

Als de speler bezig was met resizen en de muisknop wordt losgelaten, zetten we de resizing variabele weer op False. We verwijderen ook alle cards uit de workbench die niet meer in het werkgebied passen. Met return zorgen we ervoor dat de rest van de functie niet wordt uitgevoerd.

Zoals gezegd, moeten we nog de invalidate_window() functie maken. Deze functie zorgt ervoor dat de breedte van de inventory en de workbench worden aangepast aan de nieuwe breedte van het venster:

105def invalidate_window():
106   inventory_rect.width = inventory_width
107   workbench_rect.left = inventory_width
108   workbench_rect.width = WIDTH - inventory_rect.width

Run het spel. Als het goed is, kun je nu de breedte van de inventory veranderen.

../_images/resize1.png