Files
Browser/ren_browser/tabs/tabs.py
Ivan e77faa5105 Update TabsManager with overflow menu functionality
- Added support for an overflow menu in the tab bar when the number of tabs exceeds the maximum visible limit.
- Updated tab bar initialization to include scroll mode and maximum visible tabs.
- Refactored tab addition logic to ensure proper overflow handling.
2025-09-28 19:51:26 -05:00

189 lines
6.2 KiB
Python

"""Tab management system for Ren Browser.
Provides tab creation, switching, and content management functionality
for the browser interface.
"""
from types import SimpleNamespace
import flet as ft
from ren_browser.renderer.micron import render_micron
from ren_browser.renderer.plaintext import render_plaintext
class TabsManager:
"""Manages browser tabs and their content.
Handles tab creation, switching, closing, and content rendering.
"""
def __init__(self, page: ft.Page):
"""Initialize the tab manager.
Args:
page: Flet page instance for UI updates.
"""
import ren_browser.app as app_module
self.page = page
self.manager = SimpleNamespace(tabs=[], index=0)
self.tab_bar = ft.Row(
spacing=4,
scroll=ft.ScrollMode.AUTO
)
self.overflow_menu = None
self.max_visible_tabs = 8
self.content_container = ft.Container(
expand=True, bgcolor=ft.Colors.BLACK, padding=ft.padding.all(5)
)
default_content = (
render_micron("Welcome to Ren Browser")
if app_module.RENDERER == "micron"
else render_plaintext("Welcome to Ren Browser")
)
self._add_tab_internal("Home", default_content)
self.add_btn = ft.IconButton(
ft.Icons.ADD, tooltip="New Tab", on_click=self._on_add_click
)
self.close_btn = ft.IconButton(
ft.Icons.CLOSE, tooltip="Close Tab", on_click=self._on_close_click
)
self.tab_bar.controls.append(self.add_btn)
self.tab_bar.controls.append(self.close_btn)
self.select_tab(0)
self._update_overflow()
def _update_overflow(self):
"""Update overflow menu based on number of tabs."""
tab_count = len(self.manager.tabs)
if self.overflow_menu and self.overflow_menu in self.tab_bar.controls:
self.tab_bar.controls.remove(self.overflow_menu)
self.overflow_menu = None
if tab_count > self.max_visible_tabs:
overflow_items = []
for i in range(self.max_visible_tabs, tab_count):
tab_data = self.manager.tabs[i]
overflow_items.append(
ft.PopupMenuItem(
text=tab_data["title"],
on_click=lambda e, idx=i: self.select_tab(idx)
)
)
self.overflow_menu = ft.PopupMenuButton(
icon=ft.Icons.MORE_HORIZ,
tooltip=f"{tab_count - self.max_visible_tabs} more tabs",
items=overflow_items
)
insert_pos = len(self.tab_bar.controls) - 2
self.tab_bar.controls.insert(insert_pos, self.overflow_menu)
def _add_tab_internal(self, title: str, content: ft.Control):
idx = len(self.manager.tabs)
url_field = ft.TextField(
value=title,
expand=True,
text_style=ft.TextStyle(size=12),
content_padding=ft.padding.only(top=8, bottom=8, left=8, right=8),
)
go_btn = ft.IconButton(
ft.Icons.OPEN_IN_BROWSER,
tooltip="Load URL",
on_click=lambda e, i=idx: self._on_tab_go(e, i),
)
content_control = content
tab_content = ft.Column(
expand=True,
controls=[
content_control,
],
)
self.manager.tabs.append(
{
"title": title,
"url_field": url_field,
"go_btn": go_btn,
"content_control": content_control,
"content": tab_content,
}
)
tab_container = ft.Container(
content=ft.Text(title),
on_click=lambda e, i=idx: self.select_tab(i),
padding=ft.padding.symmetric(horizontal=12, vertical=6),
border_radius=5,
bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
)
insert_pos = max(0, len(self.tab_bar.controls) - 2)
self.tab_bar.controls.insert(insert_pos, tab_container)
self._update_overflow()
def _on_add_click(self, e):
title = f"Tab {len(self.manager.tabs) + 1}"
content_text = f"Content for {title}"
import ren_browser.app as app_module
content = (
render_micron(content_text)
if app_module.RENDERER == "micron"
else render_plaintext(content_text)
)
self._add_tab_internal(title, content)
self.select_tab(len(self.manager.tabs) - 1)
self._update_overflow()
self.page.update()
def _on_close_click(self, e):
if len(self.manager.tabs) <= 1:
return
idx = self.manager.index
self.manager.tabs.pop(idx)
self.tab_bar.controls.pop(idx)
for i, control in enumerate(self.tab_bar.controls[:-2]):
control.on_click = lambda e, i=i: self.select_tab(i)
new_idx = min(idx, len(self.manager.tabs) - 1)
self.select_tab(new_idx)
self._update_overflow()
self.page.update()
def select_tab(self, idx: int):
"""Select and display the tab at the given index.
Args:
idx: Index of the tab to select.
"""
self.manager.index = idx
for i, control in enumerate(self.tab_bar.controls[:-2]):
if i == idx:
control.bgcolor = ft.Colors.PRIMARY_CONTAINER
else:
control.bgcolor = ft.Colors.SURFACE_CONTAINER_HIGHEST
self.content_container.content = self.manager.tabs[idx]["content"]
self.page.update()
def _on_tab_go(self, e, idx: int):
tab = self.manager.tabs[idx]
url = tab["url_field"].value.strip()
if not url:
return
placeholder_text = f"Loading content for {url}"
import ren_browser.app as app_module
new_control = (
render_micron(placeholder_text)
if app_module.RENDERER == "micron"
else render_plaintext(placeholder_text)
)
tab["content_control"] = new_control
tab["content"].controls[0] = new_control
if self.manager.index == idx:
self.content_container.content = tab["content"]
self.page.update()