180 lines
6.1 KiB
Python
180 lines
6.1 KiB
Python
"""
|
|
ピンチ操作ハンドラー
|
|
アプリ全体でピンチイン・ズーム機能を提供する共通コンポーネント
|
|
"""
|
|
|
|
import flet as ft
|
|
from typing import Optional, Callable
|
|
|
|
class PinchHandler:
|
|
"""ピンチ操作ハンドラー"""
|
|
|
|
def __init__(self, page: ft.Page):
|
|
self.page = page
|
|
self.zoom_level = 1.0
|
|
self.min_zoom = 0.6
|
|
self.max_zoom = 2.0
|
|
self.zoom_step = 0.2
|
|
|
|
# コールバック関数
|
|
self.on_zoom_change: Optional[Callable[[float], None]] = None
|
|
self.on_tap: Optional[Callable] = None
|
|
self.on_double_tap: Optional[Callable] = None
|
|
self.on_long_press: Optional[Callable] = None
|
|
|
|
# 状態管理
|
|
self.last_distance = 0
|
|
self.is_zooming = False
|
|
|
|
def set_callbacks(self,
|
|
on_zoom_change: Optional[Callable[[float], None]] = None,
|
|
on_tap: Optional[Callable] = None,
|
|
on_double_tap: Optional[Callable] = None,
|
|
on_long_press: Optional[Callable] = None):
|
|
"""コールバック関数設定"""
|
|
self.on_zoom_change = on_zoom_change
|
|
self.on_tap = on_tap
|
|
self.on_double_tap = on_double_tap
|
|
self.on_long_press = on_long_press
|
|
|
|
def create_gesture_detector(self, content: ft.Control, on_click: Optional[Callable] = None) -> ft.GestureDetector:
|
|
"""ジェスチャー検出付きコンテナ作成"""
|
|
return ft.GestureDetector(
|
|
content=content,
|
|
on_tap=on_click
|
|
)
|
|
|
|
def _handle_tap(self, e):
|
|
"""タップ処理"""
|
|
if self.on_tap:
|
|
self.on_tap(e)
|
|
|
|
def _handle_double_tap(self, e):
|
|
"""ダブルタップ処理"""
|
|
self.zoom_in()
|
|
if self.on_double_tap:
|
|
self.on_double_tap(e)
|
|
|
|
def _handle_long_press(self, e):
|
|
"""長押し処理"""
|
|
if self.on_long_press:
|
|
self.on_long_press(e)
|
|
|
|
def _handle_tap_update(self, e):
|
|
"""タップ位置更新(ピンチ検出用)"""
|
|
# TODO: 実際のピンチ検出ロジック
|
|
# Fletの制限により、現在はボタンでのズームをメインに
|
|
pass
|
|
|
|
def zoom_in(self):
|
|
"""ズームイン"""
|
|
if self.zoom_level < self.max_zoom:
|
|
self.zoom_level += self.zoom_step
|
|
self._notify_zoom_change()
|
|
|
|
def zoom_out(self):
|
|
"""ズームアウト"""
|
|
if self.zoom_level > self.min_zoom:
|
|
self.zoom_level -= self.zoom_step
|
|
self._notify_zoom_change()
|
|
|
|
def set_zoom(self, level: float):
|
|
"""ズームレベル設定"""
|
|
self.zoom_level = max(self.min_zoom, min(self.max_zoom, level))
|
|
self._notify_zoom_change()
|
|
|
|
def reset_zoom(self):
|
|
"""ズームリセット"""
|
|
self.zoom_level = 1.0
|
|
self._notify_zoom_change()
|
|
|
|
def _notify_zoom_change(self):
|
|
"""ズーム変更通知"""
|
|
if self.on_zoom_change:
|
|
self.on_zoom_change(self.zoom_level)
|
|
|
|
def get_zoom_controls(self) -> ft.Row:
|
|
"""ズームコントロールUI作成"""
|
|
return ft.Row([
|
|
ft.IconButton(
|
|
ft.Icons.ZOOM_OUT,
|
|
icon_size=20,
|
|
tooltip="縮小",
|
|
on_click=lambda _: self.zoom_out()
|
|
),
|
|
ft.Text(f"{int(self.zoom_level * 100)}%", size=12),
|
|
ft.IconButton(
|
|
ft.Icons.ZOOM_IN,
|
|
icon_size=20,
|
|
tooltip="拡大",
|
|
on_click=lambda _: self.zoom_in()
|
|
),
|
|
ft.IconButton(
|
|
ft.Icons.REFRESH,
|
|
icon_size=20,
|
|
tooltip="リセット",
|
|
on_click=lambda _: self.reset_zoom()
|
|
)
|
|
], spacing=5)
|
|
|
|
def apply_zoom_to_size(self, base_size: float) -> float:
|
|
"""ズームをサイズに適用"""
|
|
return base_size * self.zoom_level
|
|
|
|
def apply_zoom_to_text_size(self, base_size: float) -> float:
|
|
"""ズームをテキストサイズに適用"""
|
|
return base_size * self.zoom_level
|
|
|
|
|
|
class ZoomableContainer:
|
|
"""ズーム対応コンテナ"""
|
|
|
|
def __init__(self, pinch_handler: PinchHandler):
|
|
self.pinch_handler = pinch_handler
|
|
self.base_width = 100
|
|
self.base_height = 100
|
|
self.base_text_size = 12
|
|
self.base_padding = 10
|
|
|
|
def create_zoomable_card(self,
|
|
icon: str,
|
|
title: str,
|
|
subtitle: str,
|
|
color: ft.Colors,
|
|
on_click: Optional[Callable] = None) -> ft.Container:
|
|
"""ズーム対応カード作成"""
|
|
|
|
# ズーム適用
|
|
width = self.pinch_handler.apply_zoom_to_size(self.base_width)
|
|
height = self.pinch_handler.apply_zoom_to_size(self.base_height)
|
|
text_size = self.pinch_handler.apply_zoom_to_text_size(self.base_text_size)
|
|
padding = self.pinch_handler.apply_zoom_to_size(self.base_padding)
|
|
|
|
card = ft.Container(
|
|
content=ft.Column([
|
|
ft.Container(
|
|
content=ft.Text(icon, size=text_size * 2),
|
|
width=width * 0.5,
|
|
height=height * 0.5,
|
|
bgcolor=color,
|
|
alignment=ft.alignment.Alignment(0, 0),
|
|
border_radius=padding
|
|
),
|
|
ft.Text(title, size=text_size, weight=ft.FontWeight.BOLD),
|
|
ft.Text(subtitle, size=text_size * 0.8, color=ft.Colors.GREY_600)
|
|
], spacing=5),
|
|
width=width,
|
|
height=height,
|
|
padding=padding,
|
|
bgcolor=ft.Colors.WHITE,
|
|
border_radius=padding,
|
|
shadow=ft.BoxShadow(
|
|
spread_radius=1,
|
|
blur_radius=5,
|
|
color=ft.Colors.with_opacity(0.2, ft.Colors.GREY),
|
|
offset=ft.Offset(0, 2)
|
|
),
|
|
on_click=on_click
|
|
)
|
|
|
|
return self.pinch_handler.create_gesture_detector(card)
|