commit 6625d29a0e9872c7eaccb5fc0aafbc097347b5dd Author: Nicole Rappe Date: Thu Feb 6 20:49:26 2025 -0700 Add borealis_overlay.py diff --git a/borealis_overlay.py b/borealis_overlay.py new file mode 100644 index 0000000..3e9c684 --- /dev/null +++ b/borealis_overlay.py @@ -0,0 +1,203 @@ +import sys +from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QCheckBox, QPushButton +from PyQt5.QtCore import Qt, QRect, QPoint +from PyQt5.QtGui import QPainter, QPen, QColor, QFont +from ctypes import windll + +HANDLE_SIZE = 10 # Size of the resize handle squares +LABEL_HEIGHT = 20 # Height of the label area above the rectangle + + +class Region: + def __init__(self, x, y, w, h, label="Region", color=QColor(0, 0, 255)): + self.x = x + self.y = y + self.w = w + self.h = h + self.label = label + self.color = color + + def rect(self): + return QRect(self.x, self.y, self.w, self.h) + + def label_rect(self): + """The rectangle representing the label area.""" + return QRect(self.x, self.y - LABEL_HEIGHT, self.w, LABEL_HEIGHT) + + def resize_handles(self): + """Calculate the positions of the resize handles.""" + return [ + QRect(self.x - HANDLE_SIZE // 2, self.y - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Top-left + QRect(self.x + self.w - HANDLE_SIZE // 2, self.y - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Top-right + QRect(self.x - HANDLE_SIZE // 2, self.y + self.h - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Bottom-left + QRect(self.x + self.w - HANDLE_SIZE // 2, self.y + self.h - HANDLE_SIZE // 2, HANDLE_SIZE, HANDLE_SIZE), # Bottom-right + ] + + +class OverlayCanvas(QWidget): + """ + A canvas to draw, resize, and interact with rectangular regions. + """ + def __init__(self, regions, parent=None): + super().__init__(parent) + self.regions = regions + self.edit_mode = False + self.selected_region = None + self.selected_handle = None # Track which handle is being dragged + self.drag_offset = QPoint() + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + + for region in self.regions: + # Draw the rectangle + pen = QPen(region.color) + pen.setWidth(3) + painter.setPen(pen) + painter.drawRect(region.x, region.y, region.w, region.h) + + # Draw the label above the rectangle + painter.setFont(QFont("Arial", 12, QFont.Bold)) + painter.setPen(region.color) + painter.drawText(region.x + 5, region.y - 5, region.label) + + # Draw resize handles if in edit mode + if self.edit_mode: + for handle in region.resize_handles(): + painter.fillRect(handle, region.color) + + def mousePressEvent(self, event): + if not self.edit_mode: + return # Ignore clicks if not in edit mode + + if event.button() == Qt.LeftButton: + for region in reversed(self.regions): # Check regions from topmost to bottommost + # Check if a resize handle is clicked + for i, handle in enumerate(region.resize_handles()): + if handle.contains(event.pos()): + self.selected_region = region + self.selected_handle = i + return + + # Check if the label area is clicked (for dragging) + if region.label_rect().contains(event.pos()): + self.selected_region = region + self.selected_handle = None # No resize handle, just dragging the rectangle + self.drag_offset = event.pos() - QPoint(region.x, region.y) + return + + # Check if the main rectangle is clicked (fallback for dragging) + if region.rect().contains(event.pos()): + self.selected_region = region + self.selected_handle = None + self.drag_offset = event.pos() - QPoint(region.x, region.y) + return + + def mouseMoveEvent(self, event): + if not self.edit_mode or self.selected_region is None: + return + + if self.selected_handle is None: + # Dragging the entire rectangle + self.selected_region.x = event.x() - self.drag_offset.x() + self.selected_region.y = event.y() - self.drag_offset.y() + else: + # Resizing the rectangle + if self.selected_handle == 0: # Top-left + self.selected_region.w += self.selected_region.x - event.x() + self.selected_region.h += self.selected_region.y - event.y() + self.selected_region.x = event.x() + self.selected_region.y = event.y() + elif self.selected_handle == 1: # Top-right + self.selected_region.w = event.x() - self.selected_region.x + self.selected_region.h += self.selected_region.y - event.y() + self.selected_region.y = event.y() + elif self.selected_handle == 2: # Bottom-left + self.selected_region.w += self.selected_region.x - event.x() + self.selected_region.h = event.y() - self.selected_region.y + self.selected_region.x = event.x() + elif self.selected_handle == 3: # Bottom-right + self.selected_region.w = event.x() - self.selected_region.x + self.selected_region.h = event.y() - self.selected_region.y + + # Prevent negative width/height + self.selected_region.w = max(self.selected_region.w, 10) + self.selected_region.h = max(self.selected_region.h, 10) + + self.update() # Trigger a repaint + + def mouseReleaseEvent(self, event): + if not self.edit_mode: + return + + if event.button() == Qt.LeftButton: + self.selected_region = None + self.selected_handle = None # Deselect handle + + +class BorealisOverlay(QWidget): + def __init__(self): + super().__init__() + + # Set window properties to cover the full screen + screen_geometry = QApplication.primaryScreen().geometry() + self.setGeometry(screen_geometry) + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) + self.setAttribute(Qt.WA_TranslucentBackground, True) # Transparent background + + # Use Win32 API to keep the window on top + hwnd = self.winId().__int__() + windll.user32.SetWindowPos(hwnd, -1, 0, 0, 0, 0, 0x0001 | 0x0002) + + # Create regions to draw and interact with + self.regions = [ + Region(200, 200, 150, 50, label="Region 01"), + Region(400, 300, 200, 80, label="Region 02"), + ] + + # Create canvas and attach to window + self.canvas = OverlayCanvas(self.regions, self) + self.canvas.setGeometry(self.rect()) # Match the canvas size to the full window + + # Add title, Edit Mode UI, and close button + self.init_ui() + + def init_ui(self): + """Initialize UI components.""" + # Title label + self.title_label = QLabel("Borealis Overlay", self) + self.title_label.setStyleSheet("QLabel { color: white; font-size: 18px; font-weight: bold; }") + self.title_label.move(10, 5) + + # Edit mode checkbox + self.mode_toggle = QCheckBox("Edit Mode", self) + self.mode_toggle.setStyleSheet("QCheckBox { color: white; }") + self.mode_toggle.move(10, 30) + self.mode_toggle.stateChanged.connect(self.toggle_edit_mode) + + # Close button + self.close_button = QPushButton("Close", self) + self.close_button.setStyleSheet( + "QPushButton { background-color: #40E0D0; color: white; font-weight: bold; border-radius: 5px; }" + ) # Turquoise color + self.close_button.move(10, 60) # Place it below the Edit Mode checkbox + self.close_button.clicked.connect(self.close) + + def toggle_edit_mode(self, state): + """Enable or disable edit mode for dragging and resizing rectangles.""" + editing = (state == 2) + self.canvas.edit_mode = editing # Pass the state to the canvas + print(f"Borealis Overlay Edit Mode: {'ON' if editing else 'OFF'}") # Debugging output + + +def main(): + app = QApplication(sys.argv) + window = BorealisOverlay() + window.setWindowTitle("Borealis Overlay") # Set application window title + window.show() # Explicitly show the window + sys.exit(app.exec_()) + + +if __name__ == "__main__": + main()