-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTrackBar.py
More file actions
181 lines (139 loc) · 7 KB
/
TrackBar.py
File metadata and controls
181 lines (139 loc) · 7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import pygame
from Config import WHITE, fonts, WINDOW_H, TRACK_COLORS, SURFACE_DARK
from UIComponents import Button
from utils import load_icon
add_icon = load_icon("images/add_24dp_828282_FILL1_wght300_GRAD0_opsz24.png")
menu_icon = load_icon("images/more_vert_24dp_828282_FILL1_wght300_GRAD0_opsz24.png")
class TrackBar:
def __init__(self, midi_manager, piano_roll):
self.midi_manager = midi_manager
self.piano_roll = piano_roll
self.tab_height = 40
screen_w = pygame.display.get_surface().get_width()
self.tab_width = (screen_w - 80) // 10
self.rect = pygame.Rect(0, WINDOW_H - self.tab_height, screen_w, self.tab_height)
self.tabs = []
self.plus_button = Button(0, 0, 30, 30, icon=add_icon, color=(30, 30, 33), callback=self.add_track)
self.refresh_tabs()
def refresh_tabs(self):
self.tabs = []
screen_w = pygame.display.get_surface().get_width()
available_w = screen_w - 60
num_tracks = len(self.midi_manager.track_names)
display_count = max(10, num_tracks)
self.tab_width = (available_w // display_count) - 5
self.tab_width = max(60, self.tab_width)
for i, name in enumerate(self.midi_manager.track_names):
x_pos = 10 + i * (self.tab_width + 5)
tab_rect = pygame.Rect(x_pos, self.rect.y, self.tab_width, self.tab_height)
self.tabs.append(Tab(i, name, tab_rect, self.midi_manager))
plus_x = 15 + len(self.tabs) * (self.tab_width + 5)
self.plus_button.rect.topleft = (plus_x, self.rect.y + 5)
def resize(self, w, h):
self.rect = pygame.Rect(0, h - self.tab_height, w, self.tab_height)
self.refresh_tabs()
def draw(self, screen):
pygame.draw.rect(screen, (30, 30, 33), self.rect)
pygame.draw.line(screen, (60, 60, 65), (self.rect.x, self.rect.y), (self.rect.right, self.rect.y), 1)
for tab in self.tabs:
is_active = (tab.index == self.midi_manager.active_track_index)
tab.draw(screen, is_active)
self.plus_button.draw(screen)
def handle_event(self, event):
if self.plus_button.handle_event(event):
return True # This prevents the double-trigger
for tab in self.tabs:
result = tab.handle_event(event)
if result:
if result == "select":
self.midi_manager.selected_tracks = {tab.index}
self.midi_manager.select_track(tab.index)
self.piano_roll.notes_need_update = True
return True # Consume event
return result # Return "settings" or "mute" to main.py
return False
def add_track(self):
# Strict cap at 10
if len(self.midi_manager.tracks) >= 10:
return
self.midi_manager.save_history()
# Add new empty track data
self.midi_manager.tracks.append([])
self.midi_manager.track_names.append(f"Track {len(self.midi_manager.tracks)}")
# Ensure metadata arrays are extended to match
self.midi_manager.track_volumes.append(0.8)
self.midi_manager.mute_states.append(False)
self.midi_manager.track_programs.append(0)
self.midi_manager.track_banks.append(0)
self.midi_manager.track_sfids.append(None)
self.midi_manager.sustain_states.append(False) # Add this
self.refresh_tabs()
new_index = len(self.midi_manager.tracks) - 1
self.midi_manager.select_track(new_index)
self.piano_roll.notes_need_update = True
def remove_selected_track(self):
if len(self.midi_manager.tracks) > 1:
idx = self.midi_manager.active_track_index
self.midi_manager.tracks.pop(idx)
self.midi_manager.track_names.pop(idx)
if idx < len(self.midi_manager.mute_states):
self.midi_manager.mute_states.pop(idx)
self.midi_manager.active_track_index = max(0, idx - 1)
self.refresh_tabs()
self.piano_roll.notes_need_update = True
class Tab:
def __init__(self, index, name, rect, midi_manager):
self.index = index
self.name = name
self.rect = rect
self.midi_manager = midi_manager
# NEW: Two buttons on the right side of the tab
# Settings button on the far right
self.settings_btn = Button(self.rect.right - 28, self.rect.y + 8, 24, 24, text="",
icon=menu_icon, color=SURFACE_DARK, border_radius=2)
def draw(self, screen, is_active):
# 1. Create the temporary surface
tab_surface = pygame.Surface((self.rect.width, self.rect.height), pygame.SRCALPHA)
# 2. Draw Background and Accent
pygame.draw.rect(tab_surface, SURFACE_DARK, (0, 0, self.rect.width, self.rect.height))
track_color = TRACK_COLORS[self.index % len(TRACK_COLORS)]
pygame.draw.rect(tab_surface, track_color, (0, 0, self.rect.width, 3))
# --- PIXEL-BASED TRUNCATION LOGIC ---
# Calculate available width: Tab width - left margin (8) - button space (35)
available_text_w = self.rect.width - 43
display_name = self.name
# Check if the full name fits
text_w, _ = fonts[12].size(display_name)
if text_w > available_text_w:
# Gradually remove characters until the name + "..." fits the pixels
while text_w > available_text_w and len(display_name) > 0:
display_name = display_name[:-1]
text_w, _ = fonts[12].size(display_name + "...")
display_name += "..."
# Draw the measured text
name_surf = fonts[12].render(display_name, True, WHITE)
tab_surface.blit(name_surf, (8, 12))
# ------------------------------------
# Update and Draw Button locally
self.settings_btn.rect.topleft = (self.rect.width - 28, 8)
self.settings_btn.draw(tab_surface)
# 3. Apply the "Gray out" Overlay if the tab is not active
if not is_active:
overlay = pygame.Surface((self.rect.width, self.rect.height), pygame.SRCALPHA)
overlay.fill((30, 30, 33, 100))
tab_surface.blit(overlay, (0, 0))
# 4. Final Blit to screen
screen.blit(tab_surface, self.rect.topleft)
def handle_event(self, event):
if event.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEMOTION):
# Convert global mouse pos to local tab coordinates
local_pos = (event.pos[0] - self.rect.x, event.pos[1] - self.rect.y)
# Create a dummy event with the local position to check the button
local_event = pygame.event.Event(event.type, {'pos': local_pos, 'button': getattr(event, 'button', 1)})
if self.settings_btn.handle_event(local_event):
self.midi_manager.select_track(self.index)
return "settings"
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.rect.collidepoint(event.pos):
return "select"
return False