@@ -214,93 +214,15 @@ def paintEvent(self, event):
214214 kv_geom = self .get_kv_geometry (screen_h )
215215
216216 # Origin Y determines where bars start
217- # If Top: Start at Bottom of KV (y + h)
218- # If Bottom: Start at Top of KV (y)
219217 if kv_geom ['is_top' ]:
220218 origin_y = kv_geom ['y' ] + kv_geom ['height' ]
221219 direction = 1 # Down (Positive Y)
222220 else :
223221 origin_y = kv_geom ['y' ]
224222 direction = - 1 # Up (Negative Y)
225223
226- speed = self .settings .scroll_speed
227-
228- to_recycle = []
224+ self ._draw_active_bars (painter , current_time , screen_h , origin_y , direction )
229225
230- # Bar Drawing Loop
231- for bar in self .pool .active_bars :
232- # 1. Physics
233- delta_press = current_time - bar .press_time + Config .INPUT_LATENCY_OFFSET
234- dist_head = delta_press * speed
235-
236- if bar .release_time is None :
237- dist_tail = Config .INPUT_LATENCY_OFFSET * speed
238- else :
239- delta_release = current_time - bar .release_time + Config .INPUT_LATENCY_OFFSET
240- dist_tail = delta_release * speed
241-
242- # 2. Geometry
243- height_bar = dist_head - dist_tail
244- if height_bar < Config .BAR_HEIGHT :
245- height_bar = Config .BAR_HEIGHT
246- dist_tail = dist_head - height_bar
247-
248- # 3. Screen Position
249- # Head is the "Furthest" point from origin
250- # Tail is the "Closest" point to origin
251-
252- if direction == 1 : # Moving Down
253- # Origin is Top. Head is below Tail.
254- # Top of Rect = Tail Y
255- # Tail Y = Origin + dist_tail
256- rect_y = origin_y + dist_tail
257- else : # Moving Up
258- # Origin is Bottom. Head is above Tail.
259- # Top of Rect = Head Y
260- # Head Y = Origin - dist_head
261- rect_y = origin_y - dist_head
262-
263- # 4. Recycle Check
264- if direction == 1 :
265- if rect_y > screen_h :
266- to_recycle .append (bar )
267- continue
268- else :
269- if (rect_y + height_bar ) < 0 :
270- to_recycle .append (bar )
271- continue
272-
273- # 5. Fade Logic
274- alpha = 1.0
275- if dist_head > Config .FADE_START_Y :
276- dist_into_fade = dist_head - Config .FADE_START_Y
277- factor = 1.0 - (dist_into_fade / Config .FADE_RANGE )
278- alpha = max (0.0 , min (1.0 , factor ))
279-
280- if alpha <= 0.0 :
281- continue
282-
283- x = Config .LANE_START_X + (bar .lane_index * Config .LANE_WIDTH ) + self .settings .kv_offset_x
284-
285- # Use Custom Color
286- base_c = self .settings .bar_color
287- c = QColor (base_c )
288- # Apply fade alpha to the base alpha
289- final_alpha = alpha * (base_c .alphaF ())
290- c .setAlphaF (final_alpha )
291-
292- painter .setBrush (QBrush (c ))
293- painter .setPen (Qt .NoPen )
294- painter .drawRect (QRectF (x , rect_y , Config .BAR_WIDTH , height_bar ))
295-
296- # Recycle
297- for bar in to_recycle :
298- try :
299- self .pool .active_bars .remove (bar )
300- self .pool .recycle (bar )
301- except ValueError :
302- pass
303-
304226 # Draw KeyViewer Panel
305227 if self .settings .kv_enabled :
306228 self .draw_keyviewer (painter , kv_geom )
@@ -315,6 +237,86 @@ def paintEvent(self, event):
315237 if painter .isActive ():
316238 painter .end ()
317239
240+ def _draw_active_bars (self , painter , current_time , screen_h , origin_y , direction ):
241+ speed = self .settings .scroll_speed
242+ to_recycle = []
243+
244+ # Bar Drawing Loop
245+ for bar in self .pool .active_bars :
246+ should_recycle = self ._process_bar (painter , bar , current_time , speed , origin_y , direction , screen_h )
247+ if should_recycle :
248+ to_recycle .append (bar )
249+
250+ # Recycle
251+ for bar in to_recycle :
252+ try :
253+ self .pool .active_bars .remove (bar )
254+ self .pool .recycle (bar )
255+ except ValueError :
256+ pass
257+
258+ def _process_bar (self , painter , bar , current_time , speed , origin_y , direction , screen_h ):
259+ """
260+ Calculates bar position, handles fade, draws it.
261+ Returns True if the bar should be recycled.
262+ """
263+ # 1. Physics
264+ delta_press = current_time - bar .press_time + Config .INPUT_LATENCY_OFFSET
265+ dist_head = delta_press * speed
266+
267+ if bar .release_time is None :
268+ dist_tail = Config .INPUT_LATENCY_OFFSET * speed
269+ else :
270+ delta_release = current_time - bar .release_time + Config .INPUT_LATENCY_OFFSET
271+ dist_tail = delta_release * speed
272+
273+ # 2. Geometry
274+ height_bar = dist_head - dist_tail
275+ if height_bar < Config .BAR_HEIGHT :
276+ height_bar = Config .BAR_HEIGHT
277+ dist_tail = dist_head - height_bar
278+
279+ # 3. Screen Position
280+ if direction == 1 : # Moving Down
281+ rect_y = origin_y + dist_tail
282+ else : # Moving Up
283+ rect_y = origin_y - dist_head
284+
285+ # 4. Recycle Check
286+ if direction == 1 :
287+ if rect_y > screen_h :
288+ return True
289+ else :
290+ if (rect_y + height_bar ) < 0 :
291+ # Top of rect (visually bottom if up?)
292+ # If moving up, rect_y is the top edge.
293+ # If rect_y + height < 0, it's off top screen
294+ return True
295+
296+ # 5. Fade Logic
297+ alpha = 1.0
298+ if dist_head > Config .FADE_START_Y :
299+ dist_into_fade = dist_head - Config .FADE_START_Y
300+ factor = 1.0 - (dist_into_fade / Config .FADE_RANGE )
301+ alpha = max (0.0 , min (1.0 , factor ))
302+
303+ if alpha <= 0.0 :
304+ return False # Not visible but maybe not recycled yet (wait for off-screen)
305+
306+ x = Config .LANE_START_X + (bar .lane_index * Config .LANE_WIDTH ) + self .settings .kv_offset_x
307+
308+ # Usage Custom Color
309+ base_c = self .settings .bar_color
310+ c = QColor (base_c )
311+ final_alpha = alpha * (base_c .alphaF ())
312+ c .setAlphaF (final_alpha )
313+
314+ painter .setBrush (QBrush (c ))
315+ painter .setPen (Qt .NoPen )
316+ painter .drawRect (QRectF (x , rect_y , Config .BAR_WIDTH , height_bar ))
317+
318+ return False
319+
318320 def draw_debug (self , painter , current_time ):
319321 self .frame_count += 1
320322 if current_time - self .last_fps_time >= 1.0 :
@@ -352,63 +354,65 @@ def draw_keyviewer(self, painter, geom):
352354 ordered_keys = sorted (Config .LANE_MAP .items (), key = lambda item : item [1 ])
353355
354356 painter .setFont (QFont ("Arial" , 12 , QFont .Bold ))
355-
356- # Custom Color
357357 default_color = self .settings .bar_color
358358
359359 for k_str , lane_idx in ordered_keys :
360360 kx = Config .LANE_START_X + (lane_idx * Config .LANE_WIDTH ) + self .settings .kv_offset_x
361361 ky = start_y
362362
363- k_rect = QRectF (kx , ky , key_width , key_height )
363+ # Context for rendering a single key
364+ ctx = {
365+ 'painter' : painter ,
366+ 'lane_idx' : lane_idx ,
367+ 'k_str' : k_str ,
368+ 'rect' : QRectF (kx , ky , key_width , key_height ),
369+ 'base_color' : default_color ,
370+ 'geom' : geom
371+ }
364372
365- is_pressed = lane_idx in self .active_keys_visual
366-
367- # Draw Background
368- bg_color = QColor (default_color )
369- if is_pressed :
370- # Active: Use full alpha (or slightly lighter)
371- if bg_color .alpha () > 200 :
373+ self ._draw_key_button (ctx )
374+
375+ def _draw_key_button (self , ctx ):
376+ """Draws a single key (bg, text, count) using the provided context."""
377+ painter = ctx ['painter' ]
378+ lane_idx = ctx ['lane_idx' ]
379+ k_rect = ctx ['rect' ]
380+ base_color = ctx ['base_color' ]
381+
382+ is_pressed = lane_idx in self .active_keys_visual
383+
384+ # 1. Background
385+ bg_color = QColor (base_color )
386+ if is_pressed :
387+ if bg_color .alpha () > 200 :
372388 bg_color = bg_color .lighter (120 )
373- else :
374- # Inactive: Scale alpha down based on settings
375- current_alpha = bg_color .alpha ()
376- # Opacity is 0.0 to 1.0. If opacity is 0.2, we want 20% of current alpha.
377- opacity_factor = self .settings .kv_opacity
378- new_alpha = int (current_alpha * opacity_factor )
379- bg_color .setAlpha (new_alpha )
380-
381- painter .setBrush (QBrush (bg_color ))
382- painter .setPen (Qt .NoPen )
383- painter .drawRect (k_rect )
384-
385- # Text
386- display_text = k_str .replace ("'" , "" ).upper ()
387- if "KEY." in display_text :
388- display_text = display_text .replace ("KEY." , "" )
389+ else :
390+ current_alpha = bg_color .alpha ()
391+ opacity_factor = self .settings .kv_opacity
392+ new_alpha = int (current_alpha * opacity_factor )
393+ bg_color .setAlpha (new_alpha )
389394
390- painter .setPen (QColor ("white" ))
391- painter .drawText (k_rect , Qt .AlignCenter , display_text )
395+ painter .setBrush (QBrush (bg_color ))
396+ painter .setPen (Qt .NoPen )
397+ painter .drawRect (k_rect )
398+
399+ # 2. Text
400+ display_text = ctx ['k_str' ].replace ("'" , "" ).upper ()
401+ if "KEY." in display_text :
402+ display_text = display_text .replace ("KEY." , "" )
403+
404+ painter .setPen (QColor ("white" ))
405+ painter .drawText (k_rect , Qt .AlignCenter , display_text )
406+
407+ # 3. Count
408+ if self .settings .kv_show_counts :
409+ count_val = self .key_counts .get (lane_idx , 0 )
392410
393- # Count
394- if self .settings .kv_show_counts :
395- count_val = self .key_counts .get (lane_idx , 0 )
396-
397- # Position counts based on flow
398- if geom ['is_top' ]:
399- # Flowing Down -> Counts Below? Or Above?
400- # If Top, Usually counts inside or above.
401- # Let's put counts OPPOSITE to flow?
402- # If flow down, bar is below top.
403- # Image showed counts ABOVE bar.
404- # Let's just put counts ABOVE by default for now (y - 25)
405- count_rect = QRectF (kx , ky - 25 , key_width , 20 )
406- else :
407- # Flowing Up (KV at Bottom)
408- # Counts Above
409- count_rect = QRectF (kx , ky - 25 , key_width , 20 )
410-
411- painter .setFont (QFont ("Arial" , 10 , QFont .Bold ))
412- painter .drawText (count_rect , Qt .AlignCenter , str (count_val ))
413- painter .setFont (QFont ("Arial" , 12 , QFont .Bold ))
411+ # Position counts based on flow (Always above for now based on logic)
412+ # x is k_rect.x(), y is k_rect.y() - 25, width/height same logic
413+ count_rect = QRectF (k_rect .x (), k_rect .y () - 25 , k_rect .width (), 20 )
414+
415+ painter .setFont (QFont ("Arial" , 10 , QFont .Bold ))
416+ painter .drawText (count_rect , Qt .AlignCenter , str (count_val ))
417+ painter .setFont (QFont ("Arial" , 12 , QFont .Bold ))
414418
0 commit comments