From 1bf4fda50ac8fef7caa1e90ac13f1d9b9774393d Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 12:04:44 +0530 Subject: [PATCH 01/14] Make App launch with uwsm --- modules/launcher/plugins/applications.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/launcher/plugins/applications.py b/modules/launcher/plugins/applications.py index 50503fe..00d02bc 100644 --- a/modules/launcher/plugins/applications.py +++ b/modules/launcher/plugins/applications.py @@ -1,6 +1,7 @@ import json import re from typing import List +import subprocess from fabric.utils import DesktopApp from fabric.utils.helpers import get_desktop_applications, get_relative_path @@ -156,7 +157,10 @@ class ApplicationsPlugin(PluginBase): return bool(re.search(pattern, text, re.IGNORECASE)) def _launch_application(self, app: DesktopApp): - app.launch() + print(app.command_line) + final_command = f"hyprctl dispatch exec uwsm app -- { app.command_line}" + subprocess.Popen(final_command, shell=True) + # app.launch() def _get_all_applications(self) -> List[Result]: """Get a list of all available applications.""" -- 2.43.0 From 28e7bbbc8c1591529e2de0226047dba05b3fce31 Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 12:09:01 +0530 Subject: [PATCH 02/14] Make App launch with uwsm --- modules/launcher/plugins/applications.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/launcher/plugins/applications.py b/modules/launcher/plugins/applications.py index 00d02bc..d9463b5 100644 --- a/modules/launcher/plugins/applications.py +++ b/modules/launcher/plugins/applications.py @@ -156,10 +156,20 @@ class ApplicationsPlugin(PluginBase): pattern = ".*".join(re.escape(char) for char in query) return bool(re.search(pattern, text, re.IGNORECASE)) + import subprocess + def _launch_application(self, app: DesktopApp): - print(app.command_line) - final_command = f"hyprctl dispatch exec uwsm app -- { app.command_line}" + print("Original:", app.command_line) + + # Remove ALL % codes (e.g., %u, %U, %f, %F, %i, %c, etc.) + cleaned_command = re.sub(r"%\w+", "", app.command_line).strip() + + print("Cleaned:", cleaned_command) + + # Final command with hyprctl dispatch + final_command = f"hyprctl dispatch exec 'uwsm app -- {cleaned_command}'" subprocess.Popen(final_command, shell=True) + # app.launch() def _get_all_applications(self) -> List[Result]: -- 2.43.0 From 95846f5eb27ee12342e911f9c89e9c7a3eb02f8b Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 12:27:20 +0530 Subject: [PATCH 03/14] Added Close Button instead of swipe --- modules/notification/notification.py | 254 +++------------------------ 1 file changed, 29 insertions(+), 225 deletions(-) diff --git a/modules/notification/notification.py b/modules/notification/notification.py index 2377158..6be8a07 100644 --- a/modules/notification/notification.py +++ b/modules/notification/notification.py @@ -180,26 +180,15 @@ class NotificationWidget(Box): ], ), Box(h_expand=True), - Box( - orientation="v", - children=[ - self.create_close_button() if self.show_close_button else Box(), - Box(v_expand=True), - ], + Button( + name="notification-close-button", + image=CustomImage(icon_name="close-symbolic", icon_size=18), + visible=True, # Initially hidden + on_clicked=lambda *_: self.notification.close("dismissed-by-user"), ), ], ) - def create_close_button(self): - if self.close_button is None: - self.close_button = Button( - name="notification-close-button", - visible=False, # Initially hidden - on_clicked=lambda *_: self.notification.close("dismissed-by-user"), - child=Label(label="×", name="close-button-label"), - ) - return self.close_button - def _on_enter_notify(self, widget, event): self._is_hovered = True if self.close_button: @@ -297,18 +286,20 @@ class NotificationRevealer(SlideRevealer): self._is_closing = False # Enhanced swipe detection variables for Android-style animation - self._drag_start_x = 0 self._drag_start_y = 0 + self._drag_start_x = 0 self._is_dragging = False self._swipe_threshold = 80 # Distance to trigger auto-dismiss - self._swipe_velocity_threshold = 150 # Velocity to trigger dismiss even on shorter swipes + self._swipe_velocity_threshold = ( + 150 # Velocity to trigger dismiss even on shorter swipes + ) self._swipe_in_progress = False self._current_offset = 0 self._last_drag_time = 0 self._drag_velocity = 0 self._spring_back_duration = 200 # Duration for spring-back animation self._dismiss_threshold = 0.3 # Dismiss if swiped 30% of width - + # Animation state self._animation_in_progress = False self._spring_timer_id = None @@ -324,15 +315,10 @@ class NotificationRevealer(SlideRevealer): child=self.notif_box, ) - self.event_box.connect("button-press-event", self._on_button_press) - self.event_box.connect("button-release-event", self._on_button_release) - self.event_box.connect("motion-notify-event", self._on_motion) - self.event_box.connect("realize", self._on_realize) - super().__init__( child=self.event_box, direction="right", - duration=350, # Optimized duration for macOS-like feel + duration=600, # Optimized duration for macOS-like feel ) smooth_revealer_animation(self) @@ -340,90 +326,7 @@ class NotificationRevealer(SlideRevealer): # Connect our own handler that manages the slide animation self.notification.connect("closed", self.on_resolved) - def _on_realize(self, widget): - """Setup CSS provider when widget is realized""" - try: - self._css_provider = Gtk.CssProvider() - context = widget.get_style_context() - context.add_provider(self._css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) - except Exception as e: - logger.warning(f"Could not setup CSS provider for notification animation: {e}") - - def _apply_transform(self, offset_x, opacity=1.0, scale=1.0): - """Apply transform to the notification widget for smooth animation""" - if not self.event_box or not self.event_box.get_realized(): - return - - # Clamp values - offset_x = max(0, offset_x) # Only allow rightward movement - opacity = max(0.1, min(1.0, opacity)) # Keep some visibility - scale = max(0.8, min(1.0, scale)) # Subtle scale effect - - try: - if self._css_provider: - # Apply CSS transform using the provider - css_data = f""" - * {{ - margin-left: {int(offset_x)}px; - opacity: {opacity}; - transform: scale({scale}); - }} - """ - self._css_provider.load_from_data(css_data.encode('utf-8')) - else: - # Fallback: use widget margins for offset - current_margin = self.event_box.get_margin_start() - if current_margin != int(offset_x): - self.event_box.set_margin_start(int(offset_x)) - - # Set opacity directly on the widget - self.event_box.set_opacity(opacity) - - except Exception as e: - # Final fallback: just use margin - try: - self.event_box.set_margin_start(int(offset_x)) - self.event_box.set_opacity(opacity) - except: - pass - - def _animate_spring_back(self, start_offset, target_offset=0, duration=None): - """Animate the notification springing back to its original position""" - if duration is None: - duration = self._spring_back_duration - - if self._spring_timer_id: - GLib.source_remove(self._spring_timer_id) - - start_time = GLib.get_monotonic_time() / 1000 - offset_diff = target_offset - start_offset - - def animate_step(): - current_time = GLib.get_monotonic_time() / 1000 - elapsed = current_time - start_time - progress = min(1.0, elapsed / duration) - - # Use easing function for spring-like animation - eased_progress = self._ease_out_back(progress) - current_offset = start_offset + (offset_diff * eased_progress) - - # Calculate opacity and scale based on offset - max_offset = NOTIFICATION_WIDTH * 0.8 - offset_ratio = abs(current_offset) / max_offset - opacity = 1.0 - (offset_ratio * 0.3) # Fade slightly - scale = 1.0 - (offset_ratio * 0.05) # Slight scale down - - self._apply_transform(current_offset, opacity, scale) - - if progress >= 1.0: - self._animation_in_progress = False - self._current_offset = target_offset - return False - - return True - self._animation_in_progress = True - self._spring_timer_id = GLib.timeout_add(16, animate_step) # ~60fps def _ease_out_back(self, t): """Easing function for spring-back animation""" @@ -435,28 +338,30 @@ class NotificationRevealer(SlideRevealer): """Animate the notification sliding out completely - now much faster""" target_offset = NOTIFICATION_WIDTH + 50 # Slide completely out of view duration = 120 # Much faster dismiss animation (was 250) - + if self._spring_timer_id: GLib.source_remove(self._spring_timer_id) - + start_time = GLib.get_monotonic_time() / 1000 offset_diff = target_offset - start_offset - + def animate_step(): current_time = GLib.get_monotonic_time() / 1000 elapsed = current_time - start_time progress = min(1.0, elapsed / duration) - + # Use faster easing for snappier exit - eased_progress = progress * progress # Quadratic ease out - faster than cubic + eased_progress = ( + progress * progress + ) # Quadratic ease out - faster than cubic current_offset = start_offset + (offset_diff * eased_progress) - + # Fade out and scale down during dismiss opacity = 1.0 - (progress * 1.2) # Faster fade out - scale = 1.0 - (progress * 0.15) # Slightly more scale change - + scale = 1.0 - (progress * 0.15) # Slightly more scale change + self._apply_transform(current_offset, opacity, scale) - + if progress >= 1.0: # Trigger the actual notification dismissal immediately try: @@ -464,22 +369,24 @@ class NotificationRevealer(SlideRevealer): except: pass return False - + return True - + self._animation_in_progress = True - self._spring_timer_id = GLib.timeout_add(12, animate_step) # Higher fps for smoother animation + self._spring_timer_id = GLib.timeout_add( + 12, animate_step + ) # Higher fps for smoother animation def _calculate_drag_velocity(self, current_x): """Calculate the velocity of the drag gesture""" current_time = GLib.get_monotonic_time() / 1000 - + if self._last_drag_time > 0: time_diff = current_time - self._last_drag_time if time_diff > 0: distance_diff = current_x - self._drag_start_x - self._current_offset self._drag_velocity = abs(distance_diff / time_diff) - + self._last_drag_time = current_time def _on_animation_complete(self, is_hiding=False): @@ -523,109 +430,6 @@ class NotificationRevealer(SlideRevealer): timeout_duration = 80 if self._swipe_in_progress else (self.duration + 30) GLib.timeout_add(timeout_duration, lambda: self._on_animation_complete(True)) - def _on_button_press(self, _widget, event): - if event.button == 1: - self._drag_start_x = event.x - self._drag_start_y = event.y - self._is_dragging = True - self._swipe_in_progress = False - self._current_offset = 0 - self._last_drag_time = GLib.get_monotonic_time() / 1000 - self._drag_velocity = 0 - - # Stop any ongoing animations - if self._spring_timer_id: - GLib.source_remove(self._spring_timer_id) - self._animation_in_progress = False - return False - - def _on_button_release(self, _widget, event): - if event.button == 3: # Right click - try: - if self.parent_window: - self.parent_window.clear_notification_queue() - # Also dismiss current notification - self.notification.close("dismissed-by-user") - except: - pass # Ignore errors - return True - - elif self._is_dragging and event.button == 1: - self._is_dragging = False - - # Calculate final swipe metrics - dx = event.x - self._drag_start_x - dy = abs(event.y - self._drag_start_y) - - # Calculate dismiss threshold based on notification width - dismiss_distance = NOTIFICATION_WIDTH * self._dismiss_threshold - - # Determine if we should dismiss or spring back - should_dismiss = ( - (dx > dismiss_distance and dy < 60) or # Dragged far enough - (dx > 30 and self._drag_velocity > self._swipe_velocity_threshold and dy < 60) # Fast swipe - ) - - if should_dismiss: - try: - self._swipe_in_progress = True - # For fast dismissal, if already dragged far enough, close immediately - if dx > NOTIFICATION_WIDTH * 0.6: - # Skip animation and close immediately for very large swipes - self.notification.close("dismissed-by-user") - else: - # Animate the dismiss for smaller swipes - self._animate_dismiss(self._current_offset) - logger.debug(f"Notification dismissed by swipe: dx={dx}, velocity={self._drag_velocity}") - except Exception as e: - logger.error(f"Error dismissing notification by swipe: {e}") - else: - # Spring back to original position - if abs(self._current_offset) > 5: # Only animate if there's noticeable displacement - self._animate_spring_back(self._current_offset, 0) - else: - self._apply_transform(0, 1.0, 1.0) - self._current_offset = 0 - return False - - def _on_motion(self, _widget, event): - if self._is_dragging and not self._animation_in_progress: - # Calculate current swipe distance - dx = event.x - self._drag_start_x - dy = abs(event.y - self._drag_start_y) - - # Only respond to primarily horizontal gestures - if dy < 60: # Vertical tolerance - # Calculate velocity for smooth interaction - self._calculate_drag_velocity(event.x) - - # Apply real-time transform - only allow rightward movement - if dx > 0: - self._current_offset = dx - - # Calculate visual feedback based on drag distance - max_offset = NOTIFICATION_WIDTH * 0.8 - offset_ratio = min(1.0, dx / max_offset) - - # Apply diminishing returns for large swipes - adjusted_offset = dx * (1.0 - offset_ratio * 0.3) - - # Calculate opacity and scale - opacity = 1.0 - (offset_ratio * 0.4) # Fade as it's dragged - scale = 1.0 - (offset_ratio * 0.1) # Slight scale down - - self._apply_transform(adjusted_offset, opacity, scale) - - # Visual indication of dismiss threshold - if dx > NOTIFICATION_WIDTH * self._dismiss_threshold: - # Could add visual cue here (like changing color) - pass - else: - # Reset to original position if dragging left - self._current_offset = 0 - self._apply_transform(0, 1.0, 1.0) - return False - def destroy(self): # Clean up CSS provider and timers if self._spring_timer_id: -- 2.43.0 From f5997996a93124bbdd2cb09dd6c7a9a635aa3b7a Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 12:30:28 +0530 Subject: [PATCH 04/14] Fix Size for empty notification --- modules/notification/notification.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/notification/notification.py b/modules/notification/notification.py index 6be8a07..8d1dc10 100644 --- a/modules/notification/notification.py +++ b/modules/notification/notification.py @@ -175,7 +175,11 @@ class NotificationWidget(Box): ellipsization="end", ) if notification.body - else Box() + else Label( + markup="", + h_align="start", + ellipsization="end", + ) ), ], ), -- 2.43.0 From d13a79059dd1536fe8056ab819f946532fbdbf4c Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 12:36:06 +0530 Subject: [PATCH 05/14] Added Default notification icon --- config/assets/icons/notification.png | Bin 0 -> 11368 bytes modules/notification/notification.py | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 config/assets/icons/notification.png diff --git a/config/assets/icons/notification.png b/config/assets/icons/notification.png new file mode 100644 index 0000000000000000000000000000000000000000..41d66d634ef8c9be261a207ab719608ff8af74e8 GIT binary patch literal 11368 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 Js)x4W{}29<=Cc3* literal 0 HcmV?d00001 diff --git a/modules/notification/notification.py b/modules/notification/notification.py index 8d1dc10..896dfce 100644 --- a/modules/notification/notification.py +++ b/modules/notification/notification.py @@ -1,5 +1,6 @@ import os +from fabric.utils import get_relative_path from gi.repository import Gdk, GdkPixbuf, GLib, Gtk # type: ignore from loguru import logger @@ -103,7 +104,7 @@ class NotificationWidget(Box): if "file://" in notification.app_icon else Image( name="notification-icon", - icon_name="dialog-information-symbolic" or notification.app_icon, + icon_name="notifications" or notification.app_icon, icon_size=24, ) ) @@ -208,8 +209,11 @@ class NotificationWidget(Box): return False def get_pixbuf(self, icon_path, width, height): - if icon_path.startswith("file://"): + if icon_path.startswith("file://") and os.path.exists(icon_path): icon_path = icon_path[7:] + else: + icon_path = get_relative_path("../../config/assets/icons/notification.png") + logger.warning(f"Invalid icon path: {icon_path}") if not os.path.exists(icon_path): logger.warning(f"Icon path does not exist: {icon_path}") -- 2.43.0 From 347bc8703c465905362ad5b0285e2081f06b263c Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 12:38:37 +0530 Subject: [PATCH 06/14] Bugfix --- modules/notification/notification.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/notification/notification.py b/modules/notification/notification.py index 896dfce..6fea633 100644 --- a/modules/notification/notification.py +++ b/modules/notification/notification.py @@ -211,13 +211,10 @@ class NotificationWidget(Box): def get_pixbuf(self, icon_path, width, height): if icon_path.startswith("file://") and os.path.exists(icon_path): icon_path = icon_path[7:] - else: - icon_path = get_relative_path("../../config/assets/icons/notification.png") - logger.warning(f"Invalid icon path: {icon_path}") if not os.path.exists(icon_path): logger.warning(f"Icon path does not exist: {icon_path}") - return None + icon_path = get_relative_path("../../config/assets/icons/notification.png") try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(icon_path) -- 2.43.0 From 4b9a8fd916aa283a54aac14ac9e9f119e873f28e Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 13:14:41 +0530 Subject: [PATCH 07/14] Misc Fixes and CSS --- main.css | 2 +- modules/controlcenter/player.py | 39 +++++++++++++++++---------------- modules/panel/main.py | 2 +- styles/controlcenter.css | 4 +++- styles/launcher.css | 7 +++--- styles/notification-center.css | 5 ++++- styles/notification.css | 6 ++--- 7 files changed, 36 insertions(+), 29 deletions(-) diff --git a/main.css b/main.css index 58cff50..356ab00 100644 --- a/main.css +++ b/main.css @@ -33,7 +33,7 @@ tooltip { border-radius: 16px; border-color: var(--surface); background-color: var(--shadow); - animation: tooltipShow 0.25s cubic-bezier(0.5, 0.25, 0, 1); + /* animation: tooltipShow 0.25s cubic-bezier(0.5, 0.25, 0, 1); */ } @keyframes tooltipShow { diff --git a/modules/controlcenter/player.py b/modules/controlcenter/player.py index 9bea977..9c65c42 100644 --- a/modules/controlcenter/player.py +++ b/modules/controlcenter/player.py @@ -88,7 +88,6 @@ class Player(Box): f"background-image:url('{data.HOME_DIR}/.current.wall')" ) - self.children = [ self.album_cover, Box( @@ -201,9 +200,6 @@ class Player(Box): return self.duration = dur - - - def on_seek(self, sender, ratio): pos = ratio * self.duration # duration in seconds self._player.set_position(int(pos)) @@ -269,7 +265,7 @@ class Player(Box): self._is_updating = True update_func, args, kwargs = self._update_queue.pop(0) - + try: update_func(*args, **kwargs) except Exception as e: @@ -284,13 +280,14 @@ class Player(Box): """Finish current update and process next item in queue.""" self._is_updating = False self._update_timeout_id = None - + if self._update_queue: self._process_queue() return False def _handle_thumbnail(self, metadata, player): """Queue thumbnail update to prevent overlapping operations.""" + def _update_thumbnail(): keys = metadata.keys() player_name = player.props.player_name @@ -305,16 +302,24 @@ class Player(Box): logger.debug(f"Found MPRIS art for {player_name}: {art_url}") # Check for browser players and YouTube URLs - elif player_name in ["firefox", "chromium", "vivaldi", "brave"] and "xesam:url" in keys: + elif ( + player_name in ["firefox", "chromium", "vivaldi", "brave"] + and "xesam:url" in keys + ): webpage_url = metadata["xesam:url"] if webpage_url and "youtube.com" in webpage_url: # Simple YouTube thumbnail extraction import re + match = re.search(r"(?:v=|/)([a-zA-Z0-9_-]{11})", webpage_url) if match: video_id = match.group(1) - new_thumbnail = f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg" - logger.debug(f"Found YouTube thumbnail for {player_name}: {new_thumbnail}") + new_thumbnail = ( + f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg" + ) + logger.debug( + f"Found YouTube thumbnail for {player_name}: {new_thumbnail}" + ) # Only update if thumbnail has changed if new_thumbnail and new_thumbnail != self._current_thumbnail: @@ -324,20 +329,20 @@ class Player(Box): elif not new_thumbnail and self._current_thumbnail: # Reset to default if no thumbnail found self._current_thumbnail = None - self.album_cover.set_style(f"background-image:url('{data.HOME_DIR}/.current.wall')") + self.album_cover.set_style( + f"background-image:url('{data.HOME_DIR}/.current.wall')" + ) logger.debug(f"Reset to default wallpaper for {player_name}") self._queue_update(_update_thumbnail) - - def handle_next(self, player): def _do_next(): try: self._player._player.next() except Exception as e: logger.warning(f"Failed to go to next track: {e}") - + self._queue_update(_do_next) def handle_prev(self, player): @@ -346,11 +351,9 @@ class Player(Box): self._player._player.previous() except Exception as e: logger.warning(f"Failed to go to previous track: {e}") - + self._queue_update(_do_prev) - - def handle_play_pause(self, player): def _do_play_pause(): is_playing = ( @@ -431,9 +434,7 @@ class Placeholder(Box): children=[ CenterBox( name="details", - center_children=Label( - label="Nothing Playing", style="color:black;" - ), + center_children=Label(label="Nothing Playing"), ) ], ), diff --git a/modules/panel/main.py b/modules/panel/main.py index 9bfb111..6f8334a 100644 --- a/modules/panel/main.py +++ b/modules/panel/main.py @@ -41,7 +41,7 @@ class Panel(Window): self.imac = Button( name="panel-button", child=Svg( - size=23, + size=20, svg_file=get_relative_path("../../config/assets/icons/misc/logo.svg"), ), on_clicked=lambda *_: self.menubar.show_system_dropdown(self.imac), diff --git a/styles/controlcenter.css b/styles/controlcenter.css index 728553f..9e20346 100644 --- a/styles/controlcenter.css +++ b/styles/controlcenter.css @@ -5,14 +5,16 @@ margin: 0; } + #control-center-widgets { - background-color: alpha(#000, 0.3); + background-color: alpha(#fff, 0.09); box-shadow: inset 0 0 0 1px alpha(#aaa, 0.4); border: 1px solid alpha(#111, 0.4); border-radius: 1.25rem; padding: 0.5rem; } + /* Widgets */ #wifi-widget, #bluetooth-widget { diff --git a/styles/launcher.css b/styles/launcher.css index 767653c..9b32d41 100644 --- a/styles/launcher.css +++ b/styles/launcher.css @@ -1,13 +1,14 @@ #launcher { - background-color: alpha(#999, 0.3); - color: #000; + background-color: alpha(#000, 0.3); padding: 0; border-radius: 12px; min-width: 640px; border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5); } +#launcher-search{ + color: #000; +} #header_box { padding: 0px 20px 0 20px; color: #000; diff --git a/styles/notification-center.css b/styles/notification-center.css index 3e19887..4bb5106 100644 --- a/styles/notification-center.css +++ b/styles/notification-center.css @@ -14,8 +14,11 @@ margin-top: 10px; } +#noti-clear-button { + background-color: alpha(#888, 0.09); +} #noti-clear-button:hover { - background-color: var(--surface-bright); + background-color: alpha(#fff, 0.09); } /* Stacked notification group styles */ diff --git a/styles/notification.css b/styles/notification.css index 09a7598..55ed6b0 100644 --- a/styles/notification.css +++ b/styles/notification.css @@ -1,6 +1,6 @@ #notification { padding: 16px; - background-color: alpha(#000, 0.3); + background-color: alpha(#fff, 0.03); /* border: 0.5px solid alpha(#111, 0.4); */ /* box-shadow: */ /* inset 0 0 200px 0 alpha(#111, 0.5), */ @@ -15,14 +15,14 @@ } #notification-action-buttons button { - background-color: alpha(#000, 0.3); + background-color: alpha(#999, 0.3); padding: 8px; border-radius: 20px; transition: background-color 0.1s ease; } #notification-action-buttons button:hover { - background-color: var(--surface-bright); + background-color: alpha(#000, 0.3); } #notification-action-buttons button:active { -- 2.43.0 From 7f05138d143b5ed27bec23ff227c4c1a8e31a3d2 Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 15:24:27 +0530 Subject: [PATCH 08/14] Added Corrent Icon names --- config/assets/launcher.json | 179 +++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 83 deletions(-) diff --git a/config/assets/launcher.json b/config/assets/launcher.json index 6af954b..9d411e0 100644 --- a/config/assets/launcher.json +++ b/config/assets/launcher.json @@ -1,85 +1,98 @@ { - "launcher_config": { - "em": { - "icon": "emoji", - "description": "Emoji - Search and copy emojis" - }, - "clip": { - "icon": "clipboard", - "description": "Clipboard - Search and manage clipboard history" - }, - "=": { - "examples": ["= 10*5", "= 2^8", "= sin(30)"], - "icon": "calculator", - "description": "Quick Math - Fast mathematical expressions" - }, - "app": { - "icon": "apps", - "description": "Applications - Launch installed applications" - }, - "bin": { - "icon": "terminal", - "description": "Bins - Search and run executable binaries" - }, - "power": { - "icon": "shutdown", - "description": "Power - System power management and session control" - }, - "caffeine": { - "examples": ["caffeine 30m", "caffeine 1h", "caffeine 2h", "caffeine on"], - "icon": "coffee", - "description": "Caffeine - Prevent system from going idle" - }, - "sc": { - "icon": "toolbox", - "description": "Screencapture - Record screen and audio" - }, - "wall": { - "icon": "wallpapers", - "description": "Wallpapers - Set wallpapers, and tons of features" - }, - "?": { - "icon": "world", - "description": "Quick Web Search - Fast web search with question mark", - "examples": ["? cats", "? google cats", "? youtube music"] - }, - "remind": { - "icon": "timer_on", - "description": "Reminders - Set time-based reminders with notifications" - }, - "otp": { - "icon": "auth", - "description": "Manage TOTP codes and 2FA authentication" - }, - "pass": { - "icon": "key", - "description": "Password Manager - Search and manage passwords" - }, - "bm": { - "icon": "bookmark", - "description": "Bookmarks - Search and manage bookmarks" - }, - "script": { - "icon": "terminal", - "description": "Bash Scripts - Manage and execute bash scripts" - }, - "bash": { - "icon": "terminal", - "description": "Bash Scripts - Manage and execute bash scripts" - }, - "sh": { - "icon": "terminal", - "description": "Bash Scripts - Manage and execute bash scripts" - }, - "tmux": { - "icon": "terminal", - "description": "Tmux Manager - Create, attach, rename, and kill tmux sessions" - } - }, - "settings": { - "max_examples_shown": 2, - "default_icon": "apps", - "fallback_example_template": "{trigger} ", - "config_version": "1.0" - } + "launcher_config": { + "em": { + "icon": "emoji-people-symbolic", + "description": "Emoji - Search and copy emojis" + }, + "clip": { + "icon": "app.getclipboard.Clipboard", + "description": "Clipboard - Search and manage clipboard history" + }, + "=": { + "examples": [ + "= 10*5", + "= 2^8", + "= sin(30)" + ], + "icon": "calculator", + "description": "Quick Math - Fast mathematical expressions" + }, + "app": { + "icon": "apps", + "description": "Applications - Launch installed applications" + }, + "bin": { + "icon": "terminal", + "description": "Bins - Search and run executable binaries" + }, + "power": { + "icon": "shutdown", + "description": "Power - System power management and session control" + }, + "caffeine": { + "examples": [ + "caffeine 30m", + "caffeine 1h", + "caffeine 2h", + "caffeine on" + ], + "icon": "caffeine", + "description": "Caffeine - Prevent system from going idle" + }, + "sc": { + "icon": "cs-screen", + "description": "Screencapture - Record screen and audio" + }, + "wall": { + "icon": "daily-wallpaper", + "description": "Wallpapers - Set wallpapers, and tons of features" + }, + "?": { + "icon": "search-symbolic", + "description": "Quick Web Search - Fast web search with question mark", + "examples": [ + "? cats", + "? google cats", + "? youtube music" + ] + }, + "remind": { + "icon": "alarm-timer", + "description": "Reminders - Set time-based reminders with notifications" + }, + "otp": { + "icon": "auth-otp-symbolic", + "description": "Manage TOTP codes and 2FA authentication" + }, + "pass": { + "icon": "nextcloud-password-client", + "description": "Password Manager - Search and manage passwords" + }, + "bm": { + "icon": "bookmark-add-symbolic", + "description": "Bookmarks - Search and manage bookmarks" + }, + "script": { + "icon": "terminal", + "description": "Bash Scripts - Manage and execute bash scripts" + }, + "bash": { + "icon": "terminal", + "description": "Bash Scripts - Manage and execute bash scripts" + }, + "sh": { + "icon": "terminal", + "description": "Bash Scripts - Manage and execute bash scripts" + }, + "tmux": { + "icon": "terminal", + "description": "Tmux Manager - Create, attach, rename, and kill tmux sessions" + } + }, + "settings": { + "max_examples_shown": 2, + "default_icon": "apps", + "fallback_example_template": "{trigger} ", + "config_version": "1.0" + } } -- 2.43.0 From 6ac0f9fb0882a9f0a4282d3d485d555bfe3b6824 Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 15:49:15 +0530 Subject: [PATCH 09/14] Added New ICON for bluetooth paired --- .../assets/icons/applets/bluetooth-clear.svg | 7 ++-- .../icons/applets/bluetooth-off-clear.svg | 35 ++++--------------- .../assets/icons/applets/bluetooth-paired.svg | 7 ++++ modules/panel/main.py | 2 +- styles/panel.css | 1 - 5 files changed, 19 insertions(+), 33 deletions(-) mode change 100755 => 100644 config/assets/icons/applets/bluetooth-clear.svg mode change 100755 => 100644 config/assets/icons/applets/bluetooth-off-clear.svg create mode 100644 config/assets/icons/applets/bluetooth-paired.svg diff --git a/config/assets/icons/applets/bluetooth-clear.svg b/config/assets/icons/applets/bluetooth-clear.svg old mode 100755 new mode 100644 index 69e7b25..bf893b0 --- a/config/assets/icons/applets/bluetooth-clear.svg +++ b/config/assets/icons/applets/bluetooth-clear.svg @@ -1,3 +1,6 @@ - - + + + + + diff --git a/config/assets/icons/applets/bluetooth-off-clear.svg b/config/assets/icons/applets/bluetooth-off-clear.svg old mode 100755 new mode 100644 index ecc1ab0..e77c35b --- a/config/assets/icons/applets/bluetooth-off-clear.svg +++ b/config/assets/icons/applets/bluetooth-off-clear.svg @@ -1,30 +1,7 @@ - - - - - - image/svg+xml - - - - - - - + + + + diff --git a/config/assets/icons/applets/bluetooth-paired.svg b/config/assets/icons/applets/bluetooth-paired.svg new file mode 100644 index 0000000..60fe35c --- /dev/null +++ b/config/assets/icons/applets/bluetooth-paired.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/modules/panel/main.py b/modules/panel/main.py index 6f8334a..5872a61 100644 --- a/modules/panel/main.py +++ b/modules/panel/main.py @@ -41,7 +41,7 @@ class Panel(Window): self.imac = Button( name="panel-button", child=Svg( - size=20, + size=21, svg_file=get_relative_path("../../config/assets/icons/misc/logo.svg"), ), on_clicked=lambda *_: self.menubar.show_system_dropdown(self.imac), diff --git a/styles/panel.css b/styles/panel.css index 41b4a35..bd295c9 100644 --- a/styles/panel.css +++ b/styles/panel.css @@ -16,7 +16,6 @@ } #panel-button { - border-radius: 8px; margin: 0 4px 0 4px; min-width: 20px; min-height: 20px; -- 2.43.0 From b7cbeac6d46a3c280a1479d4a703eda9745694cd Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 15:56:19 +0530 Subject: [PATCH 10/14] Added Correct Sized Icon --- config/assets/icons/applets/wifi-clear.svg | 12 +++++++----- config/assets/icons/applets/wifi-off-clear.svg | 0 config/assets/icons/misc/search.svg | 6 +----- modules/panel/components/indicators.py | 13 +++++++++---- modules/panel/main.py | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) mode change 100755 => 100644 config/assets/icons/applets/wifi-clear.svg mode change 100755 => 100644 config/assets/icons/applets/wifi-off-clear.svg diff --git a/config/assets/icons/applets/wifi-clear.svg b/config/assets/icons/applets/wifi-clear.svg old mode 100755 new mode 100644 index 55e3494..6bd8a6a --- a/config/assets/icons/applets/wifi-clear.svg +++ b/config/assets/icons/applets/wifi-clear.svg @@ -1,6 +1,8 @@ - - - - - + + + + + + + diff --git a/config/assets/icons/applets/wifi-off-clear.svg b/config/assets/icons/applets/wifi-off-clear.svg old mode 100755 new mode 100644 diff --git a/config/assets/icons/misc/search.svg b/config/assets/icons/misc/search.svg index 9b396bb..73da906 100644 --- a/config/assets/icons/misc/search.svg +++ b/config/assets/icons/misc/search.svg @@ -1,5 +1 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/modules/panel/components/indicators.py b/modules/panel/components/indicators.py index fc599ff..4a46eb6 100644 --- a/modules/panel/components/indicators.py +++ b/modules/panel/components/indicators.py @@ -15,7 +15,7 @@ class BluetoothIndicator(Box): self.bluetooth = BluetoothClient() self.bt_icon = Svg( name="bt-icon", - size=23, + size=18, svg_file=get_relative_path( "../../../config/assets/icons/applets/bluetooth-clear.svg" ), @@ -49,7 +49,12 @@ class BluetoothIndicator(Box): "../../../config/assets/icons/applets/bluetooth-clear.svg" ) ) - if len(connected_devices) == 1: + if len(connected_devices) >= 1: + self.bt_icon.set_from_file( + get_relative_path( + "../../../config/assets/icons/applets/bluetooth-paired.svg" + ) + ) device = connected_devices[0] tooltip = f"Connected to {device.alias}" if device.battery_percentage > 0: @@ -111,7 +116,7 @@ class NetworkIndicator(Box): self.network_icon = Svg( name="network-icon", - size=25, + size=18, svg_file=get_relative_path( "../../../config/assets/icons/applets/wifi-clear.svg" ), @@ -236,7 +241,7 @@ class BatteryIndicator(Box): self.battery_icon = Svg( name="battery-icon", - size=22, + size=23, svg_file=get_relative_path( "../../../config/assets/icons/battery/battery-100.svg" ), diff --git a/modules/panel/main.py b/modules/panel/main.py index 5872a61..c34ef44 100644 --- a/modules/panel/main.py +++ b/modules/panel/main.py @@ -98,7 +98,7 @@ class Panel(Window): name="panel-button", on_clicked=lambda *_: self.search_apps(), child=Svg( - size=22, + size=18, svg_file=get_relative_path("../../config/assets/icons/misc/search.svg"), ), ) -- 2.43.0 From 2e9c647442ed3d12079954ca25653f933685756a Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 16:53:34 +0530 Subject: [PATCH 11/14] Added DateWidget --- main.css | 41 ++++ main.py | 7 +- modules/widget.py | 555 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 modules/widget.py diff --git a/main.css b/main.css index 356ab00..20ed279 100644 --- a/main.css +++ b/main.css @@ -71,3 +71,44 @@ menu > menuitem:hover { menu > menuitem:hover > label { color: var(--shadow); } + + + + + + + + + + + +#day label, +#month label{ + color: var(--foreground); + padding: 5px; + margin-top: 10px; + font-size: 30px; + color: alpha(#fff, 0.7); + font-family: "SF Pro Rounded"; + font-weight: 600; +} + +#date label{ + color: alpha(#fff, 0.7); + margin-top: -10px; + font-size: 90px; + font-weight: 600; + font-family: "SF Pro Rounded"; +} +#day label{ + color: alpha(#FF0000, 0.7); +} +#date{ +} +#box-widget{ + background-color: alpha(#232323, 1); + margin-top: 20px; + min-width: 170px; + min-height: 170px; + border-radius: 16px; +} diff --git a/main.py b/main.py index 7625575..c659ff5 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +from fabric.core import widgets import setproctitle from loguru import logger @@ -7,6 +8,7 @@ from fabric.utils import get_relative_path, monitor_file from modules.dock import Dock from modules.launcher.main import Launcher from modules.notification.notification import ModusNoti +from modules.widget import Deskwidgets from modules.osd import OSD # from modules.corners import Corners @@ -41,6 +43,7 @@ if __name__ == "__main__": panel.launcher = launcher osd = OSD() + widgets = Deskwidgets() # Set corners visibility based on config # corners_visible = config.get("corners_visible", True) # corners.set_visible(corners_visible) @@ -50,7 +53,9 @@ if __name__ == "__main__": _ = css_file.connect("changed", lambda *_: set_css()) # Make sure corners is added to the app - app = Application(f"{APP_NAME}", panel, dock, switcher, launcher, modusnoti, osd) + app = Application( + f"{APP_NAME}", panel, dock, switcher, launcher, modusnoti, osd, widgets + ) def set_css(): app.set_stylesheet_from_file( diff --git a/modules/widget.py b/modules/widget.py new file mode 100644 index 0000000..d66a246 --- /dev/null +++ b/modules/widget.py @@ -0,0 +1,555 @@ +import psutil +from fabric.widgets.box import Box +from fabric.widgets.label import Label +from fabric.widgets.overlay import Overlay +from fabric.widgets.datetime import DateTime +from fabric.widgets.circularprogressbar import CircularProgressBar +from widgets.wayland import WaylandWindow as Window +from fabric.utils import invoke_repeater +import requests +import urllib.parse +import datetime +from gi.repository import GLib +from concurrent.futures import ThreadPoolExecutor +import time +import config.data as data +from config.data import load_config +import subprocess + +executor = ThreadPoolExecutor(max_workers=4) + + +config = load_config() + + +def margin(): + return ( + data.DOCK_ICON_SIZE + 10 + if not data.DOCK_ALWAYS_OCCLUDED and data.DOCK_ENABLED + else 0 + ) + + +def get_location(): + """Fetch location from config file or IP API asynchronously.""" + for attempt in range(5): + try: + response = requests.get("https://ipinfo.io/json", timeout=3) + if response.status_code == 200: + return response.json().get("city", "").replace(" ", "") + except requests.RequestException as e: + print(f"Error getting location: {e}") + if attempt < 4: + time.sleep(10) + else: + return "" + + +def get_location_async(callback): + """Fetch location asynchronously to prevent UI freeze.""" + executor.submit(lambda: GLib.idle_add(callback, get_location())) + + +def get_weather(callback): + """Fetch weather data asynchronously and update UI.""" + + def fetch_weather(): + location = get_location() + if not location: + return GLib.idle_add(callback, None) + + encoded_location = urllib.parse.quote(location) + url = f"https://wttr.in/{encoded_location}?format=j1" + urlemoji = f"https://wttr.in/{encoded_location}?format=%c" + + for attempt in range(5): + try: + response = requests.get(urlemoji, timeout=3) + responseinfo = requests.get(url, timeout=3).json() + + if response.status_code == 200: + temp_unit = "C" + temp = ( + responseinfo["current_condition"][0][f"temp_{temp_unit}"] + "°" + ) + feels_like = ( + responseinfo["current_condition"][0][f"FeelsLike{temp_unit}"] + + "°" + ) + condition = responseinfo["current_condition"][0]["weatherDesc"][0][ + "value" + ] + location = responseinfo["nearest_area"][0]["areaName"][0]["value"] + emoji = response.text.strip() + update_time = datetime.datetime.now().strftime("%I:%M:%S %p") + + GLib.idle_add( + callback, + [emoji, temp, condition, feels_like, location, update_time], + ) + return + except requests.RequestException as e: + print(f"Error fetching weather (attempt {attempt + 1}): {e}") + if attempt < 4: + time.sleep(10) + else: + GLib.idle_add(callback, None) + return + + executor.submit(fetch_weather) + + +def update_weather(widget): + def fetch_and_update(): + get_weather(lambda weather_info: update_widget(widget, weather_info)) + return True + + GLib.timeout_add_seconds(600, fetch_and_update) + fetch_and_update() + + +def update_widget(widget, weather_info): + if weather_info: + widget.weatherinfo = weather_info + widget.update_labels(weather_info) + + +class Sysinfo(Box): + @staticmethod + def bake_progress_bar(name: str = "progress-bar", size: int = 45, **kwargs): + return CircularProgressBar( + name=name, + start_angle=180, + end_angle=540, + min_value=0, + max_value=100, + size=size, + **kwargs, + ) + + @staticmethod + def bake_progress_icon(**kwargs): + return Label(**kwargs).build().add_style_class("progress-icon").unwrap() + + def __init__(self, **kwargs): + super().__init__( + layer="bottom", + title="sysinfo", + name="sysinfo", + visible=False, + all_visible=False, + **kwargs, + ) + + self.cpu_progress = self.bake_progress_bar() + self.ram_progress = self.bake_progress_bar() + self.bat_circular = self.bake_progress_bar().build().set_value(42).unwrap() + + self.progress_container = Box( + name="progress-bar-container", + spacing=12, + children=[ + Box( + children=[ + Overlay( + child=self.cpu_progress, + tooltip_text="", + overlays=[ + self.bake_progress_icon( + label="", + name="progress-icon-cpu", + ) + ], + ), + ], + ), + Box( + children=[ + Overlay( + child=self.ram_progress, + tooltip_text="", + overlays=[ + self.bake_progress_icon( + name="progress-icon-ram", + label="󰘚", + ) + ], + ) + ] + ), + Box( + children=[ + Overlay( + child=self.bat_circular, + tooltip_text="", + overlays=[ + self.bake_progress_icon( + label="󱊣", + name="progress-icon-bat", + ) + ], + ), + ], + ), + ], + ) + + self.update_status() + invoke_repeater(1000, self.update_status) + + self.add( + Box( + name="progress-bar-container-main", + orientation="v", + spacing=24, + children=[self.progress_container], + ), + ) + self.show_all() + + def update_status(self): + """Update system info asynchronously to prevent UI lag.""" + + def update(): + cpu = psutil.cpu_percent() + ram = psutil.virtual_memory().percent + battery = ( + psutil.sensors_battery().percent if psutil.sensors_battery() else 80 + ) + GLib.idle_add(self.cpu_progress.set_value, cpu) + GLib.idle_add(self.ram_progress.set_value, ram) + GLib.idle_add(self.bat_circular.set_value, battery) + + GLib.idle_add(self.cpu_progress.set_tooltip_text, f"{str(round(cpu))}%") + GLib.idle_add(self.ram_progress.set_tooltip_text, f"{str(round(ram))}%") + GLib.idle_add(self.bat_circular.set_tooltip_text, f"{str(round(battery))}%") + + executor.submit(update) + return True + + +class WeatherWidget(Box): + def __init__(self, **kwargs): + super().__init__( + layer="bottom", + name="weather-widget", + anchor="center", + h_align="center", + v_align="center", + h_expand=True, + visible=False, + v_expand=True, + **kwargs, + ) + self.weatherinfo = None + self.header = Box( + name="header1", + orientation="h", + h_expand=True, + children=[ + Box( + orientation="v", + children=[ + Label(label="", h_align="start", name="weather-location"), + Label(label="", h_align="start", name="weather-update-time"), + Label(label="", h_align="start", name="weather-condition"), + ], + ), + ], + ) + self.temp = Box( + name="header2", + orientation="h", + h_align="center", + children=[ + Box( + orientation="v", + children=[ + Label(label="", name="weather-current-temperature"), + Label(label="", name="weather-feels-like-temperature"), + ], + ), + ], + ) + self.header_right = Box( + name="header3", + orientation="h", + children=[ + Box( + orientation="v", + children=[ + Label(label="", name="weather-emoji"), + ], + ), + ], + ) + self.add( + Box( + name="window-inner", + orientation="h", + h_align="center", + v_align="center", + children=[self.header, self.temp, self.header_right], + ), + ) + self.set_visible(False) + update_weather(self) + + def update_labels(self, weather_info): + if not self.weatherinfo: + return + + # Unpack weather info into variables for better readability + emoji, temp, condition, feels_like, location, update_time = self.weatherinfo + + # Store references to deeply nested children to avoid repeated lookups + header_left = self.header.children[0].children + temp_labels = self.temp.children[0].children + header_right = self.header_right.children[0].children + + # Update labels efficiently + self.set_visible(True) + header_left[0].set_label(location) + header_left[1].set_label(f"Updated {update_time}") + header_left[2].set_label(condition) + temp_labels[0].set_label(temp) + temp_labels[1].set_label(f"Feels like {feels_like}") + header_right[0].set_label(emoji) + + +def fetch_quote(callback): + """Fetch quotes asynchronously.""" + + def fetch(): + quotes_type = "stoic" + + url = ( + "https://stoic-quotes.com/api/quote" + if quotes_type == "stoic" + else "https://zenquotes.io/api/random" + ) + + for attempt in range(5): + try: + response = requests.get(url, timeout=3) + response.raise_for_status() + respdata = response.json() + quote = ( + f"{respdata[0]['q']} - {respdata[0]['a']}" + if quotes_type == "zen" + else f"{respdata['text']} - {respdata['author']}" + ) + break + except requests.RequestException as e: + print(f"Error fetching quote: {e}") + if attempt < 4: + time.sleep(10) + else: + try: + result = subprocess.run( + ["hyprctl", "splash"], + capture_output=True, + text=True, + check=True, + ) + quote = result.stdout.strip() + " - Team Hyprland" + except subprocess.CalledProcessError as e: + print(f"Error fetching quote from hyprctl: {e}") + quote = "I learn from the mistakes of people who take my advice - Trix" + + GLib.idle_add(callback, quote) + + executor.submit(fetch) + + +def fetch_quote_async(callback): + GLib.idle_add(lambda: fetch_quote(callback)) + + +class QuoteWidget(Label): + def __init__(self, **kwargs): + super().__init__( + name="quote", + label="", + anchor="center", + h_align="center", + v_align="center", + h_expand=True, + justification="center", + v_expand=True, + visible=False, + ) + fetch_quote_async(self.update_label) + + def update_label(self, quote): + """Update quote asynchronously.""" + max_width = 150 # Set the maximum width for the quote + if len(quote) > max_width: + words = quote.split() + line1, line2 = "", "" + for word in words: + if len(line1) + len(word) + 1 <= max_width: + line1 += word + " " + else: + line2 += word + " " + quote = line1.strip() + "\n" + line2.strip() + self.set_label(quote) + self.set_visible(True) + + +class ActivationMainText(Label): + def __init__(self, **kwargs): + super().__init__( + name="activation-main-text", + label="", + anchor="bottom right", + justification="left", + v_align="start", + h_align="start", + h_expand=True, + v_expand=True, + visible=False, + ) + self.set_label("Activate Linux") + + +class ActivationSubText(Label): + def __init__(self, **kwargs): + super().__init__( + name="activation-sub-text", + label="", + anchor="bottom right", + justification="left", + v_align="start", + h_align="start", + h_expand=True, + v_expand=True, + visible=False, + ) + self.set_label("Go to Settings to activate Linux") + + +def create_widgets(config): + widgets = [] + if config.get("widgets_displaytype_visible", True): + if config.get("widgets_clock_visible", True): + widgets.append( + DateTime(formatters=["%A, %d %B"], interval=10000, name="date") + ) + if config.get("widgets_date_visible", True): + widgets.append(DateTime(formatters=["%I:%M %p"], name="clock")) + if config.get("widgets_quote_visible", True): + widgets.append(QuoteWidget()) + if config.get("widgets_weatherwid_visible", True): + widgets.append(WeatherWidget()) + else: + widgets.append(DateTime(formatters=["%I:%M %p"], name="clock")) + widgets.append(DateTime(formatters=["%A. %d %B"], interval=10000, name="date")) + return widgets + + +# if data.DESKTOP_WIDGETS: +# if config.get("widgets_displaytype_visible", True): + + +class Date(Box): + def __init__(self, **kwargs): + super().__init__( + name="date-widget", + h_expand=True, + v_expand=True, + justification="center", + h_align="center", + v_align="start", + orientation="v", + **kwargs, + ) + + self.top = Box(orientation="h", name="date-top", h_expand=True) + self.dateone = DateTime(formatters=["%a"], interval=10000, name="day") + self.datetwo = DateTime(formatters=["%b"], interval=10000, name="month") + self.datethree = DateTime(formatters=["%-d"], interval=10000, name="date") + self.top.add(self.dateone) + self.top.add(self.datetwo) + self.add(self.top) + self.add(self.datethree) + + +class Deskwidgets(Window): + def __init__(self, **kwargs): + config = load_config() + super().__init__( + name="desktop", + layer="bottom", + title="desktop-widgets", + exclusivity="none", + child=[ + Window( + anchor="top left", + layer="bottom", + child=[ + Box( + orientation="v", + name="box-widget", + v_expand=True, + v_align="center", + h_align="center", + children=[Date()], + ) + ], + ) + ], + all_visible=True, + **kwargs, + ) + + +# else: +# +# class Deskwidgets(Window): +# def __init__(self, **kwargs): +# config = load_config() +# super().__init__(name="desktop", **kwargs) +# desktop_widget = Window( +# layer="bottom", +# anchor="bottom left", +# v_align="start", +# h_align="start", +# h_expand=True, +# v_expand=True, +# justification="left", +# exclusivity="none", +# child=Box( +# orientation="v", +# children=create_widgets(config), +# ), +# all_visible=True, +# ) +# if config.get("widgets_activation_visible", True): +# activationnag = Window( +# name="activation", +# anchor="bottom right", +# layer="top", +# justification="left", +# v_align="start", +# h_align="start", +# h_expand=True, +# v_expand=True, +# child=Box( +# orientation="v", +# children=[ +# ActivationMainText(), +# ActivationSubText(), +# ], +# ), +# all_visible=True, +# ) +# else: +# activationnag = None +# +# else: +# +# class Deskwidgets(Window): +# def __init__(self, **kwargs): +# super().__init__(name="desktop", **kwargs) +# self.set_visible(False) -- 2.43.0 From 144d0a230e81db304c37b9991632fc9f3c249133 Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 17:54:53 +0530 Subject: [PATCH 12/14] Added Weather Widgets --- main.css | 62 +++++++++++++++++++++++--- modules/widget.py | 109 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 154 insertions(+), 17 deletions(-) diff --git a/main.css b/main.css index 20ed279..3cdb2ed 100644 --- a/main.css +++ b/main.css @@ -82,6 +82,56 @@ menu > menuitem:hover > label { +#weather-widget label{ + font-family: "SF Pro Rounded"; + /* font-size: 20px; */ + font-weight: 600; + margin-left: 10px; +} + +#city { + margin-top: 10px; + font-size: 20px; + +} +#temperature { + font-size: 50px; +} + +#condition-emoji{ + margin-top: 10px; + font-size: 16px; +} + +#condition { + font-size: 15px; +} +#feels-like { + + font-size: 15px; +} +/* # */ + + + + + + + + + + + + + + + + + +#date-widget label { + font-family: "SF Pro Rounded"; + margin-top: 10px; +} #day label, #month label{ color: var(--foreground); @@ -89,7 +139,6 @@ menu > menuitem:hover > label { margin-top: 10px; font-size: 30px; color: alpha(#fff, 0.7); - font-family: "SF Pro Rounded"; font-weight: 600; } @@ -98,16 +147,19 @@ menu > menuitem:hover > label { margin-top: -10px; font-size: 90px; font-weight: 600; - font-family: "SF Pro Rounded"; } #day label{ color: alpha(#FF0000, 0.7); } -#date{ -} #box-widget{ background-color: alpha(#232323, 1); - margin-top: 20px; + margin: 20px 20px 20px 20px; + min-width: 170px; + min-height: 170px; + border-radius: 16px; +} +#box-widget-2{ + background-color: alpha(#425D8A, 1); min-width: 170px; min-height: 170px; border-radius: 16px; diff --git a/modules/widget.py b/modules/widget.py index d66a246..b44e402 100644 --- a/modules/widget.py +++ b/modules/widget.py @@ -79,14 +79,26 @@ def get_weather(callback): condition = responseinfo["current_condition"][0]["weatherDesc"][0][ "value" ] + maxtemp = responseinfo["weather"][0]["maxtempC"] + "°" + mintemp = responseinfo["weather"][0]["mintempC"] + "°" location = responseinfo["nearest_area"][0]["areaName"][0]["value"] emoji = response.text.strip() update_time = datetime.datetime.now().strftime("%I:%M:%S %p") GLib.idle_add( callback, - [emoji, temp, condition, feels_like, location, update_time], + [ + emoji, + temp, + condition, + feels_like, + location, + update_time, + maxtemp, + mintemp, + ], ) + print(emoji, temp, condition, feels_like, location, update_time) return except requests.RequestException as e: print(f"Error fetching weather (attempt {attempt + 1}): {e}") @@ -448,9 +460,67 @@ def create_widgets(config): return widgets -# if data.DESKTOP_WIDGETS: -# if config.get("widgets_displaytype_visible", True): +class Weather(Box): + def __init__(self, parent, **kwargs): + super().__init__( + name="weather-widget", + h_expand=True, + v_expand=True, + justification="right", + orientation="v", + all_visible=False, + **kwargs, + ) + self.parent = parent + self.city = Label( + name="city", label="New York", justification="right", h_align="start" + ) + self.temperature = Label(name="temperature", label="36", h_align="start") + self.condition_em = Label(name="condition-emoji", label="☁️", h_align="start") + self.condition = Label(name="condition", label="Cloudy", h_align="start") + self.feels_like = Label(name="feels-like", label="H:28 L:20", h_align="start") + self.add(self.city) + self.add(self.temperature) + self.add(self.condition_em) + self.add(self.condition) + self.add(self.feels_like) + update_weather(self) + + def update_labels(self, weather_info): + if not self.weatherinfo: + return + + # Unpack weather info into variables for better readability + emoji, temp, condition, feels_like, location, update_time, maxtemp, mintemp = ( + self.weatherinfo + ) + maxmin = f"H:{maxtemp} L:{mintemp}" + + # Store references to deeply nested children to avoid repeated lookups + + self.city.set_label(location) + self.feels_like.set_label(maxmin) + self.condition.set_label(condition) + self.temperature.set_label(temp) + self.condition_em.set_label(emoji) + self.parent.set_visible(True) + + class WeatherContainer(Box): + def __init__(self, **kwargs): + super().__init__( + orientation="v", + name="box-widget-2", + v_expand=True, + v_align="center", + visible=False, + h_align="center", + children=[ + Weather(), + ], + **kwargs, + ) + self.add(WeatherWidget()) class Date(Box): def __init__(self, **kwargs): @@ -475,6 +545,8 @@ class Date(Box): self.add(self.datethree) + + class Deskwidgets(Window): def __init__(self, **kwargs): config = load_config() @@ -482,24 +554,37 @@ class Deskwidgets(Window): name="desktop", layer="bottom", title="desktop-widgets", + orientation="v", exclusivity="none", child=[ Window( anchor="top left", + orientation="h", layer="bottom", child=[ Box( - orientation="v", - name="box-widget", - v_expand=True, - v_align="center", - h_align="center", - children=[Date()], - ) + name="desktop-widgets-container", + children=[ + Box( + orientation="v", + name="box-widget", + v_expand=True, + v_align="center", + h_align="center", + children=[Date()], + ), + WeatherContainer(), + ], + ), ], - ) + ), + Window( + anchor="bottom left", + orientation="h", + layer="bottom", + child=[], + ), ], - all_visible=True, **kwargs, ) -- 2.43.0 From 916f344c897a00bc1a191cd25f5b5aa6148c45e8 Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 17:55:21 +0530 Subject: [PATCH 13/14] Fix --- modules/widget.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/widget.py b/modules/widget.py index b44e402..e9b5f63 100644 --- a/modules/widget.py +++ b/modules/widget.py @@ -506,7 +506,8 @@ class Weather(Box): self.condition_em.set_label(emoji) self.parent.set_visible(True) - class WeatherContainer(Box): + +class WeatherContainer(Box): def __init__(self, **kwargs): super().__init__( orientation="v", @@ -516,12 +517,13 @@ class Weather(Box): visible=False, h_align="center", children=[ - Weather(), + Weather(self), ], **kwargs, ) self.add(WeatherWidget()) + class Date(Box): def __init__(self, **kwargs): super().__init__( @@ -545,8 +547,6 @@ class Date(Box): self.add(self.datethree) - - class Deskwidgets(Window): def __init__(self, **kwargs): config = load_config() -- 2.43.0 From b40a25b0612b138f99ef1b6dd02d782381173512 Mon Sep 17 00:00:00 2001 From: tr1x_em Date: Sun, 3 Aug 2025 18:13:15 +0530 Subject: [PATCH 14/14] Fix Weather Value --- modules/widget.py | 106 ++-------------------------------------------- 1 file changed, 4 insertions(+), 102 deletions(-) diff --git a/modules/widget.py b/modules/widget.py index e9b5f63..2d8deab 100644 --- a/modules/widget.py +++ b/modules/widget.py @@ -72,10 +72,6 @@ def get_weather(callback): temp = ( responseinfo["current_condition"][0][f"temp_{temp_unit}"] + "°" ) - feels_like = ( - responseinfo["current_condition"][0][f"FeelsLike{temp_unit}"] - + "°" - ) condition = responseinfo["current_condition"][0]["weatherDesc"][0][ "value" ] @@ -83,7 +79,6 @@ def get_weather(callback): mintemp = responseinfo["weather"][0]["mintempC"] + "°" location = responseinfo["nearest_area"][0]["areaName"][0]["value"] emoji = response.text.strip() - update_time = datetime.datetime.now().strftime("%I:%M:%S %p") GLib.idle_add( callback, @@ -91,14 +86,11 @@ def get_weather(callback): emoji, temp, condition, - feels_like, location, - update_time, maxtemp, mintemp, ], ) - print(emoji, temp, condition, feels_like, location, update_time) return except requests.RequestException as e: print(f"Error fetching weather (attempt {attempt + 1}): {e}") @@ -240,95 +232,6 @@ class Sysinfo(Box): return True -class WeatherWidget(Box): - def __init__(self, **kwargs): - super().__init__( - layer="bottom", - name="weather-widget", - anchor="center", - h_align="center", - v_align="center", - h_expand=True, - visible=False, - v_expand=True, - **kwargs, - ) - self.weatherinfo = None - self.header = Box( - name="header1", - orientation="h", - h_expand=True, - children=[ - Box( - orientation="v", - children=[ - Label(label="", h_align="start", name="weather-location"), - Label(label="", h_align="start", name="weather-update-time"), - Label(label="", h_align="start", name="weather-condition"), - ], - ), - ], - ) - self.temp = Box( - name="header2", - orientation="h", - h_align="center", - children=[ - Box( - orientation="v", - children=[ - Label(label="", name="weather-current-temperature"), - Label(label="", name="weather-feels-like-temperature"), - ], - ), - ], - ) - self.header_right = Box( - name="header3", - orientation="h", - children=[ - Box( - orientation="v", - children=[ - Label(label="", name="weather-emoji"), - ], - ), - ], - ) - self.add( - Box( - name="window-inner", - orientation="h", - h_align="center", - v_align="center", - children=[self.header, self.temp, self.header_right], - ), - ) - self.set_visible(False) - update_weather(self) - - def update_labels(self, weather_info): - if not self.weatherinfo: - return - - # Unpack weather info into variables for better readability - emoji, temp, condition, feels_like, location, update_time = self.weatherinfo - - # Store references to deeply nested children to avoid repeated lookups - header_left = self.header.children[0].children - temp_labels = self.temp.children[0].children - header_right = self.header_right.children[0].children - - # Update labels efficiently - self.set_visible(True) - header_left[0].set_label(location) - header_left[1].set_label(f"Updated {update_time}") - header_left[2].set_label(condition) - temp_labels[0].set_label(temp) - temp_labels[1].set_label(f"Feels like {feels_like}") - header_right[0].set_label(emoji) - - def fetch_quote(callback): """Fetch quotes asynchronously.""" @@ -492,9 +395,7 @@ class Weather(Box): return # Unpack weather info into variables for better readability - emoji, temp, condition, feels_like, location, update_time, maxtemp, mintemp = ( - self.weatherinfo - ) + emoji, temp, condition, location, maxtemp, mintemp = self.weatherinfo maxmin = f"H:{maxtemp} L:{mintemp}" # Store references to deeply nested children to avoid repeated lookups @@ -514,14 +415,15 @@ class WeatherContainer(Box): name="box-widget-2", v_expand=True, v_align="center", - visible=False, + visible=True, h_align="center", children=[ Weather(self), ], **kwargs, ) - self.add(WeatherWidget()) + # self.set_style("background-color: rgba(0, 0, 0, 0.5);") + # self.add(Weather()) class Date(Box): -- 2.43.0