import gi gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") from gi.repository import Gdk, Gtk class ControlPanel(Gtk.Box): def __init__(self): super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=5) # Place the control panel in the top right self.set_halign(Gtk.Align.END) self.set_valign(Gtk.Align.START) # Add the .control-panel CSS class to this widget context = self.get_style_context() context.add_class("control-panel") def add_group(self, group): self.pack_start(group, False, False, 0) class ControlPanelGroup(Gtk.Expander): def __init__(self, title: str): Gtk.Expander.__init__(self, label=title) self._inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) # Set the size request to 200 pixels wide self.set_size_request(200, -1) # Add a bit of margin after the header self._inner.set_margin_top(5) self.add(self._inner) def add_row(self, widget): self._inner.pack_start(widget, False, False, 0) class MyControlPanel(ControlPanel): def __init__(self): super().__init__() self._first_panel = ControlPanelGroup("Some Buttons") self._first_panel.add_row(Gtk.Button(label="Button 1")) self._first_panel.add_row(Gtk.Button(label="Button 2")) self.add_group(self._first_panel) self._second_panel = ControlPanelGroup("Extra Settings") self._second_panel.add_row(Gtk.Button(label="Button 3")) self._second_panel.add_row(Gtk.Button(label="Button 4")) self._second_panel.add_row(Gtk.CheckButton.new_with_label("First checkbox")) self._second_panel.add_row(Gtk.CheckButton.new_with_label("Second checkbox")) combo = Gtk.ComboBoxText() combo.append("first", "First Choice") combo.append("second", "Second Choice") combo.append("third", "Third Choice") combo.append("forth", "This one is quite long") combo.set_active_id("first") self._second_panel.add_row(combo) self.add_group(self._second_panel) class MapEditor(Gtk.DrawingArea): def __init__(self): super().__init__() self.set_events( Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK ) self._camera_x = 0 # The X coordinate of the camera self._camera_y = 0 # The Y coordinate of the camera self._mouse_x = 0 # The X coordinate of the pointer self._mouse_y = 0 # The Y coordinate of the pointer self._button = None # The currently held mouse button # Connect to the draw and mouse events self.connect("draw", self.on_draw) self.connect("button-press-event", self.on_button_press_event) self.connect("button-release-event", self.on_button_release_event) self.connect("motion-notify-event", self.on_motion_notify_event) def on_button_press_event(self, widget, event): self._mouse_x = event.x self._mouse_y = event.y self._button = event.button def on_button_release_event(self, widget, event): self._mouse_x = 0 self._mouse_y = 0 self._button = None def on_motion_notify_event(self, widget, event): if self._button == Gdk.BUTTON_SECONDARY: self._camera_x += event.x - self._mouse_x self._camera_y += event.y - self._mouse_y self._mouse_x = event.x self._mouse_y = event.y self.queue_draw() def on_draw(self, widget, context): # Get the width and height of the widget width = self.get_allocated_width() height = self.get_allocated_height() # Render a rectangle for the background of the editor context.set_source_rgb(0.11, 0.11, 0.11) context.rectangle(0, 0, width, height) context.fill() # Render our grids context.set_line_width(1) for color, step in [(0.2, 10), (0.3, 100)]: context.set_source_rgb(color, color, color) y = int(self._camera_y % step) + 0.5 while y < height: context.move_to(0, y) context.line_to(width, y) y += step x = int(self._camera_x % step) + 0.5 while x < width: context.move_to(x, 0) context.line_to(x, height) x += step context.stroke() class MainWindow(Gtk.Window): def __init__(self): super().__init__() self.set_title("Collapsible Controls Overlay Demo") # Set the default window size to 800x800 self.set_default_size(800, 800) # When our window is destroyed, exit the main event loop self.connect("destroy", Gtk.main_quit) # Create our map editor and control panel instances self._editor = MapEditor() self._controls = MyControlPanel() # Create the overlay widget self._overlay = Gtk.Overlay() # Add our editor and control panel as overlays self._overlay.add_overlay(self._editor) self._overlay.add_overlay(self._controls) # Add the overlay as the immediate child of the window self.add(self._overlay) def install_css(): screen = Gdk.Screen.get_default() provider = Gtk.CssProvider() provider.load_from_data( b""" .control-panel { background-color: rgba(255,255,255,0.8); padding: 4px; border-bottom-left-radius: 4px; } """ ) Gtk.StyleContext.add_provider_for_screen(screen, provider, 600) install_css() window = MainWindow() window.show_all() Gtk.main()