def get_end_point( scroll: Gtk.ScrolledWindow, target: Gtk.Widget) -> int: talloc = target.get_allocation() adjustment = scroll.get_vadjustment() start_point = adjustment.get_value() page_size = adjustment.get_page_size() if start_point <= talloc.y \ and (talloc.y+talloc.height) <= (start_point+page_size): # If all parts of the target widget content are visible, # no need to animate the scroll. return -1 else: if talloc.y > start_point: # If the height of the target widget is greater than the # height of its container, scroll to the top-left # coordinates of the widget. Otherwise, scroll to the # bottom-right coordinates of the widget. if talloc.height > page_size: end_point = talloc.y else: end_point = min(adjustment.get_upper()-page_size, talloc.y+talloc.height-page_size) else: end_point = talloc.y return end_point
def scroll_to(scroll: Gtk.ScrolledWindow, target: Gtk.Widget, duration: int = 600) -> bool: """Animate the scroll for duration in milliseconds.""" target = target.get_allocation() adjustment = scroll.get_vadjustment() start_point = adjustment.get_value() page_size = adjustment.get_page_size() if start_point <= target.y \ and (target.y+target.height) <= (start_point+page_size): # If all parts of the target widget content are visible, # no need to animate the scroll. return False else: if target.y > start_point: # If the height of the target widget is greater than the # height of its container, scroll to the top-left coordinates # of the widget. Otherwise, scroll to the bottom-right # coordinates of the widget. if target.height > page_size: end_point = target.y else: end_point = min(adjustment.get_upper() - page_size, target.y + target.height - page_size) else: end_point = target.y # Stop the current animating when the same widget requested to be # animated again before it has finished animating scroll.end_point = end_point frame_clock = scroll.get_frame_clock() start_time = frame_clock.get_frame_time() end_time = start_time + 1000 * duration def animate(widget: Gtk.Widget, frame_clock: Gdk.FrameClock) -> bool: current_time = frame_clock.get_frame_time() if current_time < end_time \ and adjustment.get_value() != end_point \ and widget.end_point == end_point: t = (current_time - start_time) / (end_time - start_time) t = Animation.ease_out_cubic(t) adjustment.set_value(start_point + t * (end_point - start_point)) return GLib.SOURCE_CONTINUE else: return GLib.SOURCE_REMOVE scroll.add_tick_callback(animate) return False
def draw(widget: Gtk.Widget, cr: cairo.Context): if not world.map: return player_x = world.player.x player_y = world.player.y allocation = widget.get_allocation() tile_width = allocation.width / world.map_width tile_height = allocation.height / world.map_height # clear drawing cr.set_source_rgb(1, 1, 1) cr.paint() # draw shadow shadow_surface = cairo.ImageSurface(cairo.Format.ARGB32, allocation.width, allocation.height) scr = cairo.Context(shadow_surface) scr.set_line_width(1) scr.set_source_rgb(0, 0, 0) for y, row in enumerate(world.map): for x, tile in enumerate(row): if tile == "#": left = x * tile_width top = y * tile_height right = left + tile_width bottom = top + tile_height shadow_points = [] for dest_x, dest_y in itertools.product([left, right], [top, bottom]): other_x = {left: right, right: left}[dest_x] other_y = {top: bottom, bottom: top}[dest_y] if line_goes_through_border(player_x, player_y, dest_x, dest_y, other_x, top, bottom) \ or line_goes_through_border(player_y, player_x, dest_y, dest_x, other_y, left, right): continue shadow_points.append((dest_x, dest_y)) if len(shadow_points) == 3: for i in range(len(shadow_points)): if shadow_points[i] == (shadow_points[(i - 1) % 3][0], shadow_points[(i + 1) % 3][1]) \ or shadow_points[i] == (shadow_points[(i + 1) % 3][0], shadow_points[(i - 1) % 3][1]): del shadow_points[i] break elif len(shadow_points) == 4: continue for i, shadow_point in enumerate( extend_shadow_points(shadow_points, allocation)): if i == 0: scr.move_to(*shadow_point) else: scr.line_to(*shadow_point) scr.fill() cr.set_source_surface(shadow_surface) cr.mask(cairo.SolidPattern(0, 0, 0, .6)) cr.fill() # draw tiles cr.set_line_width(1) for y, row in enumerate(world.map): for x, tile in enumerate(row): if tile == "#": cr.set_source_rgb(.9, 0, 0) cr.rectangle(x * tile_width, y * tile_height, tile_width, tile_height) cr.fill_preserve() cr.set_source_rgba(0, 0, 0) cr.stroke() for player in world.players.values(): collides = False for y, row in enumerate(world.map): for x, tile in enumerate(row): if tile == "#": left = x * tile_width top = y * tile_height right = left + tile_width bottom = top + tile_height if collision_rect_line(Rectangle(left, right, top, bottom), player_x, player_y, player.x, player.y): collides = True break if collides: break if not collides: draw_player(cr, player) draw_player( cr, Player(player_x, player_y, world.player.rotation, world.player.health)) for bullet in world.bullets: draw_bullet(cr, bullet)