|  | 
|  | 1 | +import flet as ft | 
|  | 2 | + | 
|  | 3 | +class Card(ft.GestureDetector): | 
|  | 4 | + def __init__(self, solitaire, suite, rank): | 
|  | 5 | + super().__init__() | 
|  | 6 | + self.solitaire = solitaire | 
|  | 7 | + self.suite = suite | 
|  | 8 | + self.rank = rank | 
|  | 9 | + self.face_up = False | 
|  | 10 | + self.slot = None | 
|  | 11 | + | 
|  | 12 | + self.mouse_cursor = ft.MouseCursor.MOVE | 
|  | 13 | + self.drag_interval = 5 | 
|  | 14 | + self.on_pan_update = self.drag | 
|  | 15 | + self.on_pan_start = self.start_drag | 
|  | 16 | + self.on_pan_end = self.drop | 
|  | 17 | + self.on_tap = self.click | 
|  | 18 | + self.on_double_tap = self.doubleclick | 
|  | 19 | + self.content = ft.Container( | 
|  | 20 | + width=70, | 
|  | 21 | + height=100, | 
|  | 22 | + border_radius=ft.border_radius.all(6), | 
|  | 23 | + #content=ft.Image(src=f"/images/card_back.svg"), | 
|  | 24 | + content=ft.Image(src=self.solitaire.settings.card_back) | 
|  | 25 | + ) | 
|  | 26 | + | 
|  | 27 | + def turn_face_up(self): | 
|  | 28 | + self.face_up = True | 
|  | 29 | + self.content.content.src=f"/images/{self.rank.name}_{self.suite.name}.svg" | 
|  | 30 | + self.update() | 
|  | 31 | + | 
|  | 32 | +  | 
|  | 33 | + def turn_face_down(self): | 
|  | 34 | + self.face_up = False | 
|  | 35 | + #self.content.content.src=f"/images/card_back.svg" | 
|  | 36 | + self.content.content.src=self.solitaire.settings.card_back | 
|  | 37 | + self.update() | 
|  | 38 | +  | 
|  | 39 | + def can_be_moved(self): | 
|  | 40 | + if self.face_up and self.slot.type != 'waste': | 
|  | 41 | + return True | 
|  | 42 | + if self.slot.type == 'waste' and len(self.solitaire.waste.pile)-1 == self.solitaire.waste.pile.index(self): | 
|  | 43 | + return True | 
|  | 44 | + return False | 
|  | 45 | + | 
|  | 46 | + def start_drag(self, e: ft.DragStartEvent): | 
|  | 47 | + #if e.control.face_up: | 
|  | 48 | + if self.can_be_moved(): | 
|  | 49 | + cards_to_drag = self.get_cards_to_move() | 
|  | 50 | + self.solitaire.move_on_top(cards_to_drag) | 
|  | 51 | + # remember card original position to return it back if needed | 
|  | 52 | + self.solitaire.current_top = e.control.top | 
|  | 53 | + self.solitaire.current_left = e.control.left | 
|  | 54 | + # self.page.update() | 
|  | 55 | + | 
|  | 56 | + def drag(self, e: ft.DragUpdateEvent): | 
|  | 57 | + if self.can_be_moved(): | 
|  | 58 | + i = 0 | 
|  | 59 | + for card in self.get_cards_to_move(): | 
|  | 60 | + card.top = max(0, self.top + e.delta_y) | 
|  | 61 | + if card.slot.type == "tableau": | 
|  | 62 | + card.top += i * self.solitaire.card_offset | 
|  | 63 | + card.left = max(0, self.left + e.delta_x) | 
|  | 64 | + i += 1 | 
|  | 65 | + card.update() | 
|  | 66 | + | 
|  | 67 | + def drop(self, e: ft.DragEndEvent): | 
|  | 68 | + if self.can_be_moved(): | 
|  | 69 | + cards_to_drag = self.get_cards_to_move() | 
|  | 70 | + slots = self.solitaire.tableau + self.solitaire.foundation | 
|  | 71 | + # check if card is close to any of the tableau or foundation slots | 
|  | 72 | + for slot in slots: | 
|  | 73 | + # compare with top and left position of the top card in the slot pile | 
|  | 74 | + if ( | 
|  | 75 | + abs(self.top - slot.upper_card_top()) < 40 | 
|  | 76 | + and abs(self.left - slot.left) < 40 | 
|  | 77 | + ):  | 
|  | 78 | + if ( | 
|  | 79 | + slot.type == "tableau" | 
|  | 80 | + and self.solitaire.check_tableau_rules( | 
|  | 81 | + self, slot.get_top_card() | 
|  | 82 | + ) | 
|  | 83 | + ) or ( | 
|  | 84 | + slot.type == "foundation" | 
|  | 85 | + and len(cards_to_drag) == 1 | 
|  | 86 | + and self.solitaire.check_foundation_rules( | 
|  | 87 | + self, slot.get_top_card() | 
|  | 88 | + ) | 
|  | 89 | + ): | 
|  | 90 | + | 
|  | 91 | + old_slot = self.slot | 
|  | 92 | + for card in cards_to_drag: | 
|  | 93 | + card.place(slot) | 
|  | 94 | + # reveal top card in old tableau slot if exists | 
|  | 95 | + #if len(old_slot.pile) > 0 and old_slot.type == "tableau": | 
|  | 96 | + # old_slot.get_top_card().turn_face_up() | 
|  | 97 | + self.solitaire.display_waste() | 
|  | 98 | + self.page.update() | 
|  | 99 | + | 
|  | 100 | + return | 
|  | 101 | + | 
|  | 102 | + # return card to original position | 
|  | 103 | + self.solitaire.bounce_back(cards_to_drag) | 
|  | 104 | + self.page.update() | 
|  | 105 | + | 
|  | 106 | + def doubleclick(self, e): | 
|  | 107 | + if self.slot.type in ("waste", "tableau"): | 
|  | 108 | + if self.face_up: | 
|  | 109 | + #self.move_on_top(self.solitaire.controls, [self]) | 
|  | 110 | + self.solitaire.move_on_top([self]) | 
|  | 111 | + old_slot = self.slot | 
|  | 112 | + for slot in self.solitaire.foundation: | 
|  | 113 | + if self.solitaire.check_foundation_rules(self, slot.get_top_card()): | 
|  | 114 | + # if True: | 
|  | 115 | + self.place(slot) | 
|  | 116 | + #if len(old_slot.pile) > 0: | 
|  | 117 | + #old_slot.get_top_card().turn_face_up() | 
|  | 118 | + self.solitaire.display_waste() | 
|  | 119 | + self.page.update() | 
|  | 120 | + return | 
|  | 121 | + | 
|  | 122 | + def click(self, e): | 
|  | 123 | + if self.slot.type == "stock": | 
|  | 124 | + for i in range( | 
|  | 125 | + min(self.solitaire.settings.waste_size, len(self.solitaire.stock.pile)) | 
|  | 126 | + ): | 
|  | 127 | + top_card = self.solitaire.stock.pile[-1] | 
|  | 128 | + #self.move_on_top(self.solitaire.controls, [top_card]) | 
|  | 129 | + #self.solitaire.move_on_top([top_card]) | 
|  | 130 | + top_card.place(self.solitaire.waste) | 
|  | 131 | + top_card.turn_face_up() | 
|  | 132 | + self.solitaire.display_waste() | 
|  | 133 | + self.page.update() | 
|  | 134 | + | 
|  | 135 | + if self.slot.type == "tableau": | 
|  | 136 | + if self.face_up == False and len(self.slot.pile)-1 == self.slot.pile.index(self): | 
|  | 137 | + self.turn_face_up() | 
|  | 138 | + | 
|  | 139 | + def place(self, slot): | 
|  | 140 | + self.top = slot.top | 
|  | 141 | + self.left = slot.left | 
|  | 142 | + if slot.type == "tableau": | 
|  | 143 | + self.top += self.solitaire.card_offset * len(slot.pile) | 
|  | 144 | + | 
|  | 145 | + # remove the card form the old slot's pile if exists | 
|  | 146 | + | 
|  | 147 | + if self.slot is not None: | 
|  | 148 | + # if self.slot.type == 'waste': | 
|  | 149 | + # self.solitaire.update_waste() | 
|  | 150 | + self.slot.pile.remove(self) | 
|  | 151 | + | 
|  | 152 | + # set card's slot as new slot | 
|  | 153 | + self.slot = slot | 
|  | 154 | + | 
|  | 155 | + # add the card to the new slot's pile | 
|  | 156 | + slot.pile.append(self) | 
|  | 157 | + self.solitaire.move_on_top([self]) | 
|  | 158 | + if self.solitaire.check_if_you_won(): | 
|  | 159 | + self.solitaire.on_win() | 
|  | 160 | + self.update() | 
|  | 161 | + | 
|  | 162 | + def get_cards_to_move(self): | 
|  | 163 | + """returns list of cards that will be dragged together, starting with the current card""" | 
|  | 164 | + if self.slot is not None: | 
|  | 165 | + return self.slot.pile[self.slot.pile.index(self):] | 
|  | 166 | + | 
|  | 167 | + return [self] | 
0 commit comments