Skip to content

Commit e3d27c9

Browse files
authored
Rework dot buttons (#465)
* mintinstall.py - replace dot button * Delete mintinstall-banner-dot.svg * Rework banner buttons Rework banner buttons: - Add a visual indicator for the number of banners (dot) - Merge arrow buttons and dots into a container - Add a transparent black frame to avoid visibility problems with banner colors and dots. * Undo some changes Revert ' def start_slideshow_timer ' to the original code. useless addition. * Improve spacing Add space between banner summary and nav_box
1 parent c13d459 commit e3d27c9

File tree

2 files changed

+175
-106
lines changed

2 files changed

+175
-106
lines changed

usr/lib/linuxmint/mintinstall/mintinstall.py

Lines changed: 175 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,14 @@ def __init__(self, pkginfo, installer, name, background, color, is_flatpak, app_
335335
#BannerTitle {
336336
color: %(color)s;
337337
font-weight: bold;
338-
font-size: 48px;
339-
padding-top: 12px;
338+
font-size: 44px;
339+
padding-top: 8px;
340340
}
341341
#BannerSummary {
342342
color: %(color)s;
343343
font-weight: normal;
344344
font-size: 16px;
345-
padding-top: 10px;
345+
padding-top: 4px;
346346
}
347347
#BannerFlatpakLabel {
348348
font-weight: normal;
@@ -371,7 +371,7 @@ def __init__(self, pkginfo, installer, name, background, color, is_flatpak, app_
371371

372372
image = Gtk.Image.new_from_file(self.image_uri)
373373

374-
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12, halign=Gtk.Align.START)
374+
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8, margin_bottom=17, halign=Gtk.Align.START)
375375
vbox.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
376376
vbox.set_border_width(6)
377377

@@ -969,8 +969,8 @@ def list_header_func(row, before, user_data=None):
969969
self.flowbox_featured = None
970970
self.flowbox_top_rated = None
971971
self.banner_tile = None
972-
self.banner_dot_box = None
973972
self.banner_stack = None
973+
self.banner_dots_box = None
974974
self.banner_slideshow_timeout_id = 0
975975

976976
self.package_type_store = Gtk.ListStore(int, str, str, str, object) # index, label, summary, icon-name, remotename, pkginfo
@@ -1090,6 +1090,11 @@ def print_startup_time(self):
10901090

10911091
@print_timing
10921092
def load_banner(self):
1093+
"""
1094+
Load and configure the banner display with navigation controls.
1095+
The banner shows featured applications in a slideshow format.
1096+
"""
1097+
# Get the main banner container
10931098
box = self.builder.get_object("box_banner")
10941099

10951100
if self.low_res:
@@ -1100,9 +1105,11 @@ def load_banner(self):
11001105
self.main_window.set_default_size(800, 500)
11011106
return
11021107

1108+
# Clear existing content
11031109
for child in box.get_children():
11041110
child.destroy()
11051111

1112+
# Setup main container and stack
11061113
overlay = Gtk.Overlay()
11071114
box.pack_start(overlay, True, True, 0)
11081115

@@ -1111,17 +1118,110 @@ def load_banner(self):
11111118
self.banner_stack.set_transition_duration(BANNER_TIMER)
11121119
overlay.add(self.banner_stack)
11131120

1114-
self.banner_dot_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
1115-
halign=Gtk.Align.CENTER,
1116-
valign=Gtk.Align.END)
1117-
overlay.add_overlay(self.banner_dot_box)
1118-
1121+
# Create navigation buttons
1122+
left_arrow = Gtk.Button()
1123+
right_arrow = Gtk.Button()
1124+
left_image = Gtk.Image.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
1125+
right_image = Gtk.Image.new_from_icon_name("go-next-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
1126+
1127+
# Setup button styling
1128+
button_style_override = """
1129+
button {
1130+
background-color: transparent;
1131+
border: none;
1132+
padding: 0;
1133+
margin: 0;
1134+
min-height: 16px;
1135+
min-width: 16px;
1136+
}
1137+
button:hover image {
1138+
color: rgb(255, 255, 255);
1139+
}
1140+
button image {
1141+
color: rgba(255, 255, 255, 0.6);
1142+
}
1143+
"""
1144+
css_provider = Gtk.CssProvider()
1145+
css_provider.load_from_data(str.encode(button_style_override))
1146+
1147+
# Apply styling to navigation elements
1148+
for widget in (left_arrow, right_arrow, left_image, right_image):
1149+
context = widget.get_style_context()
1150+
context.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
1151+
1152+
# Configure navigation buttons
1153+
left_arrow.add(left_image)
1154+
right_arrow.add(right_image)
1155+
1156+
for button in (left_arrow, right_arrow):
1157+
button.set_relief(Gtk.ReliefStyle.NONE)
1158+
1159+
# Set navigation directions
1160+
left_arrow.direction = -1
1161+
right_arrow.direction = 1
1162+
1163+
# Connect button signals
1164+
left_arrow.connect("clicked", self.on_arrow_clicked, self.banner_stack)
1165+
right_arrow.connect("clicked", self.on_arrow_clicked, self.banner_stack)
1166+
1167+
# Create dots container (centered)
1168+
self.banner_dots_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
1169+
spacing=4,
1170+
halign=Gtk.Align.CENTER,
1171+
valign=Gtk.Align.CENTER)
1172+
1173+
# Create a background frame for navigation
1174+
dots_frame = Gtk.EventBox()
1175+
dots_frame.set_name("dots-frame")
1176+
1177+
# Add CSS styling for the frame
1178+
frame_box_css = """
1179+
#dots-frame {
1180+
background-color: rgba(0, 0, 0, 0.25);
1181+
border-radius: 12px;
1182+
}
1183+
"""
1184+
css_provider = Gtk.CssProvider()
1185+
css_provider.load_from_data(frame_box_css.encode())
1186+
dots_frame.get_style_context().add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
1187+
1188+
# Create navigation container with arrows and dots
1189+
nav_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
1190+
spacing=8,
1191+
halign=Gtk.Align.CENTER,
1192+
valign=Gtk.Align.CENTER)
1193+
1194+
# Add padding around navigation
1195+
nav_container.set_margin_start(7)
1196+
nav_container.set_margin_end(7)
1197+
nav_container.set_margin_top(4)
1198+
nav_container.set_margin_bottom(4)
1199+
1200+
# Pack everything together
1201+
nav_container.pack_start(left_arrow, False, False, 0)
1202+
nav_container.pack_start(self.banner_dots_box, True, False, 0)
1203+
nav_container.pack_start(right_arrow, False, False, 0)
1204+
1205+
dots_frame.add(nav_container)
1206+
1207+
frame_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
1208+
halign=Gtk.Align.CENTER,
1209+
valign=Gtk.Align.END,
1210+
margin_bottom=5)
1211+
frame_container.pack_start(dots_frame, True, False, 0)
1212+
overlay.add_overlay(frame_container)
1213+
1214+
self.dots = []
1215+
self.current_dot_index = 0
1216+
1217+
# Load featured applications
11191218
json_array = json.load(open("/usr/share/linuxmint/mintinstall/featured/featured.json", "r"))
11201219
random.shuffle(json_array)
11211220

11221221
selected_apps = set()
11231222
num_selected = 0
11241223

1224+
# Process featured applications
11251225
for app_json in json_array:
11261226
if num_selected >= 5:
11271227
break
@@ -1133,13 +1233,12 @@ def load_banner(self):
11331233
if name in selected_apps:
11341234
continue
11351235

1236+
# Handle Flatpak and regular applications
11361237
if name.startswith("flatpak:"):
11371238
name = name.replace("flatpak:", "")
11381239
pkginfo = self.installer.find_pkginfo(name, installer.PKG_TYPE_FLATPAK)
1139-
11401240
if pkginfo is None or not pkginfo.verified:
11411241
continue
1142-
11431242
is_flatpak = True
11441243
else:
11451244
pkginfo = self.installer.find_pkginfo(name, installer.PKG_TYPE_APT)
@@ -1148,6 +1247,7 @@ def load_banner(self):
11481247
if pkginfo is None:
11491248
continue
11501249

1250+
# Add application to banner
11511251
selected_apps.add(name)
11521252
num_selected += 1
11531253

@@ -1161,48 +1261,72 @@ def load_banner(self):
11611261

11621262
tile = BannerTile(pkginfo, self.installer, name, background, color, is_flatpak, app_json, self.on_banner_clicked)
11631263
flowbox.insert(tile, -1)
1164-
11651264
flowbox.show_all()
11661265
self.banner_stack.add_named(flowbox, str(len(self.banner_stack.get_children())))
11671266

1168-
icon = Gtk.Image.new_from_icon_name("mintinstall-banner-dot", Gtk.IconSize.MENU)
1169-
icon.set_pixel_size(5)
1170-
1171-
button_class_override = """
1172-
#BannerDotOverlay {
1173-
background-color: rgba(0, 0, 0, 0);
1174-
background-image: none;
1175-
border-color: rgba(0, 0, 0, 0);
1176-
min-height: 12px;
1177-
min-width: 22px;
1178-
-gtk-icon-shadow: none;
1179-
-gtk-icon-effect: none;
1180-
box-shadow: none;
1181-
}
1182-
"""
1183-
provider = Gtk.CssProvider()
1184-
provider.load_from_data(str.encode(button_class_override))
1185-
1186-
dot_button = Gtk.Button(
1187-
halign=Gtk.Align.CENTER,
1188-
valign=Gtk.Align.END,
1189-
name="BannerDotOverlay",
1190-
relief=Gtk.ReliefStyle.NONE,
1191-
can_focus=False,
1192-
image=icon
1193-
)
1194-
1195-
dot_button.get_style_context().add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
1196-
dot_button.connect("clicked", self.on_dot_clicked, len(self.banner_stack.get_children()) - 1)
1197-
self.banner_dot_box.pack_start(dot_button, False, False, 0)
1267+
# Create dot indicator
1268+
dot = Gtk.DrawingArea()
1269+
dot.set_size_request(10, 10)
1270+
dot.index = len(self.dots) # Store index as a property
1271+
dot.connect("draw", self.draw_dot)
1272+
self.dots.append(dot)
1273+
self.banner_dots_box.pack_start(dot, False, False, 0)
11981274

1199-
self.update_dot_buttons(0)
1275+
# Display the banner
12001276
box.show_all()
1277+
self.update_dots(0)
1278+
self.start_slideshow_timer()
12011279

1202-
def on_dot_clicked(self, button, index):
1280+
def draw_dot(self, widget, cr):
1281+
"""
1282+
Draw a circular dot indicator with outline
1283+
"""
1284+
# Get the dot's dimensions
1285+
width = widget.get_allocated_width()
1286+
height = widget.get_allocated_height()
1287+
1288+
# Calculate center and radius
1289+
center_x = width / 2
1290+
center_y = height / 2
1291+
radius = min(width, height) / 2 - 1
1292+
1293+
# Draw outline circle
1294+
cr.set_line_width(1)
1295+
cr.arc(center_x, center_y, radius, 0, 2 * math.pi)
1296+
cr.set_source_rgba(0, 0, 0, 0.25) # Semi-transparent black outline
1297+
cr.stroke_preserve()
1298+
1299+
# Fill circle
1300+
if widget.index == self.current_dot_index:
1301+
cr.set_source_rgba(1, 1, 1, 1) # Active dot: solid white
1302+
else:
1303+
cr.set_source_rgba(1, 1, 1, 0.40) # Inactive dot: semi-transparent white
1304+
1305+
cr.fill()
1306+
return False
1307+
1308+
def update_dots(self, current_index):
1309+
"""
1310+
Update the appearance of dots based on the current banner index
1311+
"""
1312+
self.current_dot_index = current_index
1313+
for dot in self.dots:
1314+
dot.queue_draw()
1315+
1316+
def on_arrow_clicked(self, button, banner_stack):
1317+
current_index = int(self.banner_stack.get_visible_child_name())
1318+
direction = getattr(button, "direction", 1)
1319+
1320+
if direction == 1: # next
1321+
self.banner_stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT)
1322+
new_index = (current_index + 1) % len(self.banner_stack.get_children())
1323+
else: # previous
1324+
self.banner_stack.set_transition_type(Gtk.StackTransitionType.SLIDE_RIGHT)
1325+
new_index = (current_index - 1) % len(self.banner_stack.get_children())
1326+
1327+
self.banner_stack.set_visible_child_name(str(new_index))
1328+
self.update_dots(new_index)
12031329
self.start_slideshow_timer()
1204-
self.banner_stack.set_visible_child_name(str(index))
1205-
self.update_dot_buttons(index)
12061330

12071331
def start_slideshow_timer(self):
12081332
if self.low_res:
@@ -1220,24 +1344,13 @@ def stop_slideshow_timer(self):
12201344
self.banner_slideshow_timeout_id = 0
12211345

12221346
def on_slideshow_timeout(self):
1223-
visible_child = self.banner_stack.get_visible_child()
1224-
index = self.banner_stack.get_children().index(visible_child)
1225-
new_index = (index + 1) % len(self.banner_stack.get_children())
1347+
current_index = int(self.banner_stack.get_visible_child_name())
1348+
self.banner_stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT)
1349+
new_index = (current_index + 1) % len(self.banner_stack.get_children())
12261350
self.banner_stack.set_visible_child_name(str(new_index))
1227-
self.update_dot_buttons(new_index)
1351+
self.update_dots(new_index)
12281352
return True
12291353

1230-
def update_dot_buttons(self, current_index):
1231-
for i, button in enumerate(self.banner_dot_box.get_children()):
1232-
if i == current_index: #Bigger do if current slide
1233-
icon = Gtk.Image.new_from_icon_name("mintinstall-banner-dot", Gtk.IconSize.MENU)
1234-
icon.set_pixel_size(10)
1235-
button.set_image(icon)
1236-
else:
1237-
icon = Gtk.Image.new_from_icon_name("mintinstall-banner-dot", Gtk.IconSize.MENU)
1238-
icon.set_pixel_size(5)
1239-
button.set_image(icon)
1240-
12411354
def on_banner_clicked(self, button, pkginfo):
12421355
self.show_package(pkginfo, self.PAGE_LANDING)
12431356

usr/share/icons/hicolor/scalable/status/mintinstall-banner-dot.svg

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)