commit
57df0e6159
6 files changed
+405
-144
No files matched your search
@@ -14,7 +14,7 @@
|
||||
all: unset;
|
||||
color: var(--foreground);
|
||||
font-size: 12px;
|
||||
font-family: unset;
|
||||
font-family: "Rubik", sans-serif;
|
||||
}
|
||||
|
||||
#corner {
|
||||
|
||||
+20
-44
@@ -17,16 +17,16 @@ SEARCH_DEBOUNCE_MS = 150
|
||||
TRIGGER_SEARCH_DEBOUNCE_MS = 50
|
||||
CURSOR_POSITION_DELAY_MS = 10
|
||||
SCROLL_PADDING = 10
|
||||
DEFAULT_ITEM_HEIGHT = 60
|
||||
DEFAULT_ITEM_HEIGHT = 68
|
||||
PAGE_NAVIGATION_STEP = 5
|
||||
LAUNCHER_WIDTH = 550
|
||||
LAUNCHER_HEIGHT = 260
|
||||
LAUNCHER_WIDTH = 640
|
||||
LAUNCHER_HEIGHT = 400
|
||||
|
||||
|
||||
class Launcher(Window):
|
||||
"""
|
||||
Main launcher window with search functionality and plugin system.
|
||||
Similar to Albert Launcher interface.
|
||||
Spotlight-style interface.
|
||||
"""
|
||||
|
||||
# Properties
|
||||
@@ -62,7 +62,7 @@ class Launcher(Window):
|
||||
# Current results and selection
|
||||
self.results: List[Result] = []
|
||||
self.selected_index = 0
|
||||
self.max_results = 10
|
||||
self.max_results = 8 # Spotlight typically shows fewer results
|
||||
|
||||
# Trigger system
|
||||
self.triggered_plugin = None # Currently active triggered plugin
|
||||
@@ -84,10 +84,10 @@ class Launcher(Window):
|
||||
)
|
||||
self.add(main_box)
|
||||
|
||||
# Search entry with overlay for trigger indication
|
||||
# Search entry with Spotlight-style large text
|
||||
self.search_entry = Entry(
|
||||
name="launcher-search",
|
||||
placeholder="Type to search...",
|
||||
placeholder="Spotlight Search",
|
||||
h_expand=True,
|
||||
h_align="fill",
|
||||
notify_text=lambda entry, *_: self._on_search_changed(entry),
|
||||
@@ -106,7 +106,7 @@ class Launcher(Window):
|
||||
|
||||
main_box.add(self.header_box)
|
||||
|
||||
# Results container
|
||||
# Results container with increased height for Spotlight-style
|
||||
self.results_scroll = ScrolledWindow(
|
||||
name="launcher-results-scroll",
|
||||
h_scrollbar_policy="never",
|
||||
@@ -644,7 +644,7 @@ class Launcher(Window):
|
||||
if getattr(self, "_initializing", True) or not hasattr(self, "results_box"):
|
||||
return
|
||||
|
||||
# Update input field with trigger indication (Albert-style)
|
||||
# Update input field with trigger indication (Spotlight-style)
|
||||
self._update_input_action_text()
|
||||
|
||||
# Clear existing results
|
||||
@@ -684,7 +684,7 @@ class Launcher(Window):
|
||||
self.results_scroll.hide()
|
||||
|
||||
def _update_input_action_text(self):
|
||||
"""Update the input field with action text (Albert-style)."""
|
||||
"""Update the input field with action text (Spotlight-style)."""
|
||||
# Check if search_entry is initialized
|
||||
if not hasattr(self, "search_entry") or self.search_entry is None:
|
||||
return
|
||||
@@ -692,51 +692,27 @@ class Launcher(Window):
|
||||
# Get the current input text
|
||||
current_text = self.search_entry.get_text()
|
||||
|
||||
# Spotlight-style placeholder text
|
||||
if self.triggered_plugin:
|
||||
if self.results:
|
||||
# Show the first result as action text
|
||||
first_result = self.results[0]
|
||||
action_text = first_result.title
|
||||
|
||||
# For calculator results, show the evaluation
|
||||
if hasattr(first_result, "data") and "result" in first_result.data:
|
||||
action_text = str(first_result.data["result"])
|
||||
|
||||
# Set placeholder to show the action text
|
||||
if action_text and action_text != current_text:
|
||||
self.search_entry.set_placeholder_text(
|
||||
f"{current_text} → {action_text}"
|
||||
)
|
||||
else:
|
||||
self.search_entry.set_placeholder_text(
|
||||
f"[{self.active_trigger.strip()}] searching..."
|
||||
)
|
||||
# Show more minimal placeholder for triggered mode
|
||||
self.search_entry.set_placeholder_text(f"Search {self.active_trigger.strip()}...")
|
||||
else:
|
||||
# In trigger mode but no results yet
|
||||
if current_text == self.active_trigger.strip():
|
||||
# Just the trigger keyword
|
||||
self.search_entry.set_placeholder_text(
|
||||
f"[{self.active_trigger.strip()}] ready - type to search"
|
||||
)
|
||||
self.search_entry.set_placeholder_text(f"Search {self.active_trigger.strip()}...")
|
||||
else:
|
||||
# Searching within trigger
|
||||
self.search_entry.set_placeholder_text(
|
||||
f"[{self.active_trigger.strip()}] searching..."
|
||||
)
|
||||
self.search_entry.set_placeholder_text(f"Searching {self.active_trigger.strip()}...")
|
||||
else:
|
||||
# Not in trigger mode - show trigger help
|
||||
# Not in trigger mode - show Spotlight-style help
|
||||
if current_text == ":":
|
||||
self.search_entry.set_placeholder_text(
|
||||
"Showing all available triggers."
|
||||
)
|
||||
self.search_entry.set_placeholder_text("Available search triggers")
|
||||
elif current_text:
|
||||
self.search_entry.set_placeholder_text(
|
||||
"Type trigger keyword (calc, app, file, system...)"
|
||||
)
|
||||
self.search_entry.set_placeholder_text("Spotlight Search")
|
||||
else:
|
||||
self.search_entry.set_placeholder_text(
|
||||
"Type trigger keyword: calc, app, file, system..."
|
||||
)
|
||||
self.search_entry.set_placeholder_text("Spotlight Search")
|
||||
|
||||
def _clear_results(self):
|
||||
"""Clear all results."""
|
||||
@@ -1116,7 +1092,7 @@ class Launcher(Window):
|
||||
# Try to get actual allocation, fallback to estimation
|
||||
try:
|
||||
allocation = selected_child.get_allocation()
|
||||
item_height = allocation.height if allocation.height > 0 else 60
|
||||
item_height = allocation.height if allocation.height > 0 else 68
|
||||
item_y = allocation.y
|
||||
except:
|
||||
# Fallback to estimation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from gi.repository import Gdk, GdkPixbuf, GLib # type: ignore
|
||||
from gi.repository import Gdk, GdkPixbuf, GLib, Gtk # type: ignore
|
||||
from loguru import logger
|
||||
|
||||
import config.data as data
|
||||
@@ -296,11 +296,23 @@ class NotificationRevealer(SlideRevealer):
|
||||
self.parent_window = parent_window
|
||||
self._is_closing = False
|
||||
|
||||
# Add swipe detection variables
|
||||
# Enhanced swipe detection variables for Android-style animation
|
||||
self._drag_start_x = 0
|
||||
self._drag_start_y = 0
|
||||
self._is_dragging = False
|
||||
self._swipe_threshold = 100 # pixels to trigger swipe dismiss
|
||||
self._swipe_threshold = 80 # Distance to trigger auto-dismiss
|
||||
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
|
||||
self._css_provider = None
|
||||
|
||||
# Wrap notification in EventBox for swipe detection
|
||||
self.event_box = EventBox(
|
||||
@@ -315,6 +327,7 @@ class NotificationRevealer(SlideRevealer):
|
||||
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,
|
||||
@@ -327,6 +340,148 @@ 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"""
|
||||
c1 = 1.70158
|
||||
c3 = c1 + 1
|
||||
return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2)
|
||||
|
||||
def _animate_dismiss(self, start_offset):
|
||||
"""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
|
||||
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
|
||||
|
||||
self._apply_transform(current_offset, opacity, scale)
|
||||
|
||||
if progress >= 1.0:
|
||||
# Trigger the actual notification dismissal immediately
|
||||
try:
|
||||
self.notification.close("dismissed-by-user")
|
||||
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
|
||||
|
||||
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):
|
||||
if is_hiding:
|
||||
# Manually destroy the notification widget since we disconnected its handler
|
||||
@@ -346,23 +501,42 @@ class NotificationRevealer(SlideRevealer):
|
||||
|
||||
self._is_closing = True
|
||||
|
||||
# Use left-to-right slide for auto-dismiss (expired), right-to-left for manual close
|
||||
# Clean up any ongoing animations
|
||||
if self._spring_timer_id:
|
||||
GLib.source_remove(self._spring_timer_id)
|
||||
|
||||
# Use different slide directions based on dismiss reason
|
||||
if reason == "expired":
|
||||
# Left-to-right slide for auto-dismiss
|
||||
# Left-to-right slide for auto-dismiss (expired)
|
||||
self.set_slide_direction("left")
|
||||
elif self._swipe_in_progress:
|
||||
# For swipe dismissals, use immediate hiding without additional slide animation
|
||||
# The swipe animation already handled the visual feedback
|
||||
self.duration = 50 # Very fast transition
|
||||
self.set_slide_direction("right")
|
||||
else:
|
||||
# Right-to-left slide for manual close
|
||||
# Right-to-left slide for manual close (button clicks)
|
||||
self.set_slide_direction("right")
|
||||
|
||||
self.hide()
|
||||
# Reduced timeout for snappier transitions
|
||||
GLib.timeout_add(self.duration + 30, lambda: self._on_animation_complete(True))
|
||||
# Reduced timeout for snappier transitions, especially for swipe dismissals
|
||||
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):
|
||||
@@ -378,26 +552,86 @@ class NotificationRevealer(SlideRevealer):
|
||||
|
||||
elif self._is_dragging and event.button == 1:
|
||||
self._is_dragging = False
|
||||
|
||||
# Calculate swipe distance
|
||||
|
||||
# Calculate final swipe metrics
|
||||
dx = event.x - self._drag_start_x
|
||||
dy = abs(event.y - self._drag_start_y)
|
||||
|
||||
# Left-to-right swipe: dismiss current notification only
|
||||
if dx > self._swipe_threshold and dy < 50: # 50px vertical tolerance
|
||||
|
||||
# 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.notification.close("dismissed-by-user")
|
||||
except:
|
||||
pass # Ignore errors
|
||||
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:
|
||||
# TODO: Add visual feedback during swipe
|
||||
# Could add translation or opacity changes here
|
||||
pass
|
||||
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:
|
||||
GLib.source_remove(self._spring_timer_id)
|
||||
super().destroy()
|
||||
|
||||
|
||||
class NotificationState:
|
||||
IDLE = 0
|
||||
@@ -411,7 +645,6 @@ class ModusNoti(Window):
|
||||
self.notifications = Box(
|
||||
v_expand=True,
|
||||
h_expand=True,
|
||||
title="Notification Center",
|
||||
style="margin: 1px 0px 1px 1px;",
|
||||
orientation="v",
|
||||
spacing=5,
|
||||
@@ -431,6 +664,7 @@ class ModusNoti(Window):
|
||||
anchor="top right",
|
||||
child=self.notifications,
|
||||
layer="overlay",
|
||||
title="noti",
|
||||
all_visible=True,
|
||||
visible=True,
|
||||
exclusive=False,
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
margin: 8px 8px 5px 8px;
|
||||
border-radius: 16px;
|
||||
/* border: 2px solid var(--surface); */
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
/* transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); */
|
||||
}
|
||||
|
||||
#dock.vertical {
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
#dock-full {
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
/* transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
padding: 4px;
|
||||
border-radius: 24px;
|
||||
/* box-shadow: 0 0 4px alpha(var(--shadow), 0.5); */
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
/* transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); */
|
||||
}
|
||||
|
||||
#dock-app-button:hover {
|
||||
|
||||
+125
-74
@@ -1,117 +1,142 @@
|
||||
#launcher {
|
||||
background: var(--shadow);
|
||||
padding: 12px;
|
||||
border-radius: 34px;
|
||||
min-width: 550px;
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
background: rgba(30, 30, 30, 0.95);
|
||||
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);
|
||||
}
|
||||
|
||||
#header_box {
|
||||
padding: 20px 20px 0 20px;
|
||||
}
|
||||
|
||||
#close-button,
|
||||
#config-button {
|
||||
background-color: var(--surface);
|
||||
border-radius: 40px;
|
||||
padding: 8px;
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
background-color: transparent;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
#close-button:hover,
|
||||
#close-button:focus,
|
||||
#config-button:hover,
|
||||
#config-button:focus {
|
||||
background-color: var(--surface-bright);
|
||||
border-radius: 16px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#close-button.focused,
|
||||
#config-button.focused {
|
||||
background-color: var(--primary);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 0 0 2px var(--primary-bright);
|
||||
background-color: rgba(0, 122, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.4);
|
||||
}
|
||||
|
||||
#close-button.focused #close-label {
|
||||
color: var(--background);
|
||||
color: #007AFF;
|
||||
}
|
||||
|
||||
#config-button.focused #config-label {
|
||||
color: var(--background);
|
||||
color: #007AFF;
|
||||
}
|
||||
|
||||
#close-button:active {
|
||||
background-color: var(--red-dim);
|
||||
border-radius: 40px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#close-label {
|
||||
color: var(--red-dim);
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#close-button:active #close-label {
|
||||
color: var(--shadow);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
#config-button:active {
|
||||
background-color: var(--primary);
|
||||
border-radius: 40px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#config-label {
|
||||
color: var(--primary);
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#config-button:active #config-label {
|
||||
color: var(--shadow);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
#launcher-icon-label {
|
||||
font-size: 32px;
|
||||
padding: 8px;
|
||||
font-size: 20px;
|
||||
padding: 6px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
#launcher-search {
|
||||
font-weight: bold;
|
||||
background-color: var(--surface);
|
||||
color: var(--foreground);
|
||||
border-radius: 16px;
|
||||
padding: 10px;
|
||||
font-weight: 400;
|
||||
font-size: 36px;
|
||||
background-color: transparent;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 12px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#launcher-search:focus {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#launcher-search selection {
|
||||
color: var(--background);
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
background-color: rgba(0, 122, 255, 0.8);
|
||||
}
|
||||
|
||||
#launcher-results-scroll {
|
||||
margin-top: 10px;
|
||||
border-radius: 16px;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
#launcher-results-scroll scrollbar {
|
||||
border-radius: 10px;
|
||||
background-color: var(--surface);
|
||||
padding: 4px;
|
||||
margin-left: 6px;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#launcher-results-scroll slider {
|
||||
border-radius: 8px;
|
||||
min-width: 10px;
|
||||
min-height: 38px;
|
||||
background-color: var(--primary);
|
||||
#launcher-results-scroll scrollbar slider {
|
||||
border-radius: 2px;
|
||||
min-width: 4px;
|
||||
min-height: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#launcher-results-scroll scrollbar:hover slider {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
#launcher-results {
|
||||
background: transparent;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#launcher-result-item {
|
||||
border-radius: 40px;
|
||||
padding: 0 16px;
|
||||
animation: loadSlot 0.5s ease;
|
||||
min-height: 52px;
|
||||
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin: 2px 0;
|
||||
min-height: 64px;
|
||||
transition: all 0.15s ease;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@keyframes loadSlot {
|
||||
@@ -123,54 +148,80 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#launcher-result-item:focus,
|
||||
#launcher-result-item:selected,
|
||||
#launcher-result-item:hover,
|
||||
#launcher-result-item.selected {
|
||||
border-radius: 16px;
|
||||
background-color: var(--surface);
|
||||
padding-left: 20px;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(0, 122, 255, 0.8);
|
||||
padding: 12px 16px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
#launcher-result-item.selected #result-item-title {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#launcher-result-item.selected #result-item-subtitle {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
#launcher-result-item.selected #result-item-plugin {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
#result-item-main {
|
||||
min-height: 48px;
|
||||
min-height: 56px;
|
||||
}
|
||||
|
||||
#launcher-result-item {
|
||||
min-height: 48px;
|
||||
min-height: 56px;
|
||||
}
|
||||
|
||||
#result-item-icon {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
margin-right: 12px;
|
||||
min-width: 56px;
|
||||
min-height: 56px;
|
||||
margin-right: 16px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
#result-item-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--foreground);
|
||||
margin-top: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
margin-top: 4px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
#result-item-subtitle {
|
||||
font-size: 12px;
|
||||
color: var(--outline);
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#result-item-plugin {
|
||||
font-size: 10px;
|
||||
color: var(--outline);
|
||||
font-style: italic;
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.7;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-style: normal;
|
||||
margin-bottom: 4px;
|
||||
opacity: 1;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
#network-password-entry {
|
||||
border: 1px solid var(--outline);
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#network-password-entry:focus {
|
||||
border: 1px solid rgba(0, 122, 255, 0.6);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#notification {
|
||||
padding: 16px;
|
||||
background-color: alpha(#000, 0.3);
|
||||
border: 0.5px solid alpha(#111, 0.4);
|
||||
/* border: 0.5px solid alpha(#111, 0.4); */
|
||||
/* box-shadow: */
|
||||
/* inset 0 0 200px 0 alpha(#111, 0.5), */
|
||||
/* inset 0 0 0 0.5px alpha(#aaa, 0.4); */
|
||||
|
||||
Reference in new issue
Block a user