```
# src/tests/test_manual_crop.py
import flet as ft

from configs.path_config import EXAMPLE_IMAGE


def main(page: ft.Page):
    rect_left = 50
    rect_top = 50
    rect_width = 200
    rect_height = 150

    rect_container = ft.Ref[ft.Container]()

    # 矩形の移動処理
    def on_rect_pan_update(e: ft.DragUpdateEvent):
        nonlocal rect_left, rect_top
        
        rect_left += e.delta_x
        rect_top += e.delta_y
        
        # 画像境界内に制限
        if rect_left < 0:
            rect_left = 0
        if rect_top < 0:
            rect_top = 0
        if rect_left + rect_width > 400:
            rect_left = 400 - rect_width
        if rect_top + rect_height > 300:
            rect_top = 300 - rect_height
            
        rect_container.current.left = rect_left
        rect_container.current.top = rect_top
        page.update()

    # 右下角のリサイズ処理
    def on_bottom_right_resize(e: ft.DragUpdateEvent):
        nonlocal rect_width, rect_height
        
        rect_width += e.delta_x
        rect_height += e.delta_y
        
        # 最小サイズと境界制限
        if rect_width < 20:
            rect_width = 20
        if rect_height < 20:
            rect_height = 20
        if rect_left + rect_width > 400:
            rect_width = 400 - rect_left
        if rect_top + rect_height > 300:
            rect_height = 300 - rect_top
            
        rect_container.current.width = rect_width
        rect_container.current.height = rect_height
        page.update()

    # 左上角のリサイズ処理
    def on_top_left_resize(e: ft.DragUpdateEvent):
        nonlocal rect_left, rect_top, rect_width, rect_height
        
        new_left = rect_left + e.delta_x
        new_top = rect_top + e.delta_y
        new_width = rect_width - e.delta_x
        new_height = rect_height - e.delta_y
        
        # 境界と最小サイズチェック
        if new_left >= 0 and new_width >= 20:
            rect_left = new_left
            rect_width = new_width
        if new_top >= 0 and new_height >= 20:
            rect_top = new_top
            rect_height = new_height
            
        rect_container.current.left = rect_left
        rect_container.current.top = rect_top
        rect_container.current.width = rect_width
        rect_container.current.height = rect_height
        page.update()

    # 右上角のリサイズ処理
    def on_top_right_resize(e: ft.DragUpdateEvent):
        nonlocal rect_top, rect_width, rect_height
        
        new_top = rect_top + e.delta_y
        new_width = rect_width + e.delta_x
        new_height = rect_height - e.delta_y
        
        # 境界と最小サイズチェック
        if new_width >= 20 and rect_left + new_width <= 400:
            rect_width = new_width
        if new_top >= 0 and new_height >= 20:
            rect_top = new_top
            rect_height = new_height
            
        rect_container.current.top = rect_top
        rect_container.current.width = rect_width
        rect_container.current.height = rect_height
        page.update()

    # 左下角のリサイズ処理
    def on_bottom_left_resize(e: ft.DragUpdateEvent):
        nonlocal rect_left, rect_width, rect_height
        
        new_left = rect_left + e.delta_x
        new_width = rect_width - e.delta_x
        new_height = rect_height + e.delta_y
        
        # 境界と最小サイズチェック
        if new_left >= 0 and new_width >= 20:
            rect_left = new_left
            rect_width = new_width
        if new_height >= 20 and rect_top + new_height <= 300:
            rect_height = new_height
            
        rect_container.current.left = rect_left
        rect_container.current.width = rect_width
        rect_container.current.height = rect_height
        page.update()

    # ハンドル作成関数
    def create_handle(cursor_type, pan_handler):
        handle = ft.Container(
            width=12,
            height=12,
            bgcolor=ft.Colors.WHITE,
            border=ft.border.all(2, ft.Colors.BLUE),
            border_radius=6,
        )
        
        return ft.GestureDetector(
            content=handle,
            mouse_cursor=cursor_type,
            drag_interval=1,
            on_pan_update=pan_handler,
        )

    # 各角のハンドル
    top_left_handle = create_handle(ft.MouseCursor.RESIZE_UP_LEFT, on_top_left_resize)
    top_right_handle = create_handle(ft.MouseCursor.RESIZE_UP_RIGHT, on_top_right_resize)
    bottom_left_handle = create_handle(ft.MouseCursor.RESIZE_DOWN_LEFT, on_bottom_left_resize)
    bottom_right_handle = create_handle(ft.MouseCursor.RESIZE_DOWN_RIGHT, on_bottom_right_resize)

    # 矩形の移動用背景
    move_area = ft.GestureDetector(
        content=ft.Container(
            width=rect_width,
            height=rect_height,
            bgcolor=ft.Colors.TRANSPARENT,
        ),
        mouse_cursor=ft.MouseCursor.MOVE,
        drag_interval=1,
        on_pan_update=on_rect_pan_update,
    )

    # リサイズ可能矩形
    crop_rect = ft.Container(
        ref=rect_container,
        left=rect_left,
        top=rect_top,
        width=rect_width,
        height=rect_height,
        border=ft.border.all(2, ft.Colors.BLUE),
        bgcolor=None,
        content=ft.Stack(
            controls=[
                # 移動用の透明エリア
                move_area,
                # 左上角のハンドル
                ft.Container(
                    content=top_left_handle,
                    alignment=ft.alignment.top_left,
                    margin=ft.margin.only(left=-6, top=-6),
                ),
                # 右上角のハンドル
                ft.Container(
                    content=top_right_handle,
                    alignment=ft.alignment.top_right,
                    margin=ft.margin.only(right=-6, top=-6),
                ),
                # 左下角のハンドル
                ft.Container(
                    content=bottom_left_handle,
                    alignment=ft.alignment.bottom_left,
                    margin=ft.margin.only(left=-6, bottom=-6),
                ),
                # 右下角のハンドル
                ft.Container(
                    content=bottom_right_handle,
                    alignment=ft.alignment.bottom_right,
                    margin=ft.margin.only(right=-6, bottom=-6),
                ),
            ]
        ),
    )

    # Stack: 背景画像 + 矩形
    stack = ft.Stack(
        controls=[
            ft.Image(
                src=EXAMPLE_IMAGE,
                width=400,
                height=300,
                fit=ft.ImageFit.CONTAIN,
            ),
            crop_rect,
        ],
        width=400,
        height=300,
        clip_behavior=ft.ClipBehavior.HARD_EDGE,
    )

    # 選択領域の座標を表示するテキスト
    coord_text = ft.Text(
        f"選択領域: x={rect_left}, y={rect_top}, width={rect_width}, height={rect_height}",
        size=12,
    )

    # 座標を更新する関数
    def update_coord_text():
        coord_text.value = f"選択領域: x={rect_left}, y={rect_top}, width={rect_width}, height={rect_height}"
        page.update()

    # 各リサイズ関数に座標更新を追加
    original_on_rect_pan_update = on_rect_pan_update
    original_on_bottom_right_resize = on_bottom_right_resize
    original_on_top_left_resize = on_top_left_resize
    original_on_top_right_resize = on_top_right_resize
    original_on_bottom_left_resize = on_bottom_left_resize

    def on_rect_pan_update(e):
        original_on_rect_pan_update(e)
        update_coord_text()

    def on_bottom_right_resize(e):
        original_on_bottom_right_resize(e)
        update_coord_text()

    def on_top_left_resize(e):
        original_on_top_left_resize(e)
        update_coord_text()

    def on_top_right_resize(e):
        original_on_top_right_resize(e)
        update_coord_text()

    def on_bottom_left_resize(e):
        original_on_bottom_left_resize(e)
        update_coord_text()

    # ハンドルを再作成（更新された関数で）
    top_left_handle = create_handle(ft.MouseCursor.RESIZE_UP_LEFT, on_top_left_resize)
    top_right_handle = create_handle(ft.MouseCursor.RESIZE_UP_RIGHT, on_top_right_resize)
    bottom_left_handle = create_handle(ft.MouseCursor.RESIZE_DOWN_LEFT, on_bottom_left_resize)
    bottom_right_handle = create_handle(ft.MouseCursor.RESIZE_DOWN_RIGHT, on_bottom_right_resize)

    move_area = ft.GestureDetector(
        content=ft.Container(
            width=rect_width,
            height=rect_height,
            bgcolor=ft.Colors.TRANSPARENT,
        ),
        mouse_cursor=ft.MouseCursor.MOVE,
        drag_interval=1,
        on_pan_update=on_rect_pan_update,
    )

    # 矩形を再構築
    crop_rect = ft.Container(
        ref=rect_container,
        left=rect_left,
        top=rect_top,
        width=rect_width,
        height=rect_height,
        border=ft.border.all(2, ft.Colors.BLUE),
        bgcolor=None,
        content=ft.Stack(
            controls=[
                move_area,
                ft.Container(
                    content=top_left_handle,
                    alignment=ft.alignment.top_left,
                    margin=ft.margin.only(left=-6, top=-6),
                ),
                ft.Container(
                    content=top_right_handle,
                    alignment=ft.alignment.top_right,
                    margin=ft.margin.only(right=-6, top=-6),
                ),
                ft.Container(
                    content=bottom_left_handle,
                    alignment=ft.alignment.bottom_left,
                    margin=ft.margin.only(left=-6, bottom=-6),
                ),
                ft.Container(
                    content=bottom_right_handle,
                    alignment=ft.alignment.bottom_right,
                    margin=ft.margin.only(right=-6, bottom=-6),
                ),
            ]
        ),
    )

    stack = ft.Stack(
        controls=[
            ft.Image(
                src=EXAMPLE_IMAGE,
                width=400,
                height=300,
                fit=ft.ImageFit.CONTAIN,
            ),
            crop_rect,
        ],
        width=400,
        height=300,
        clip_behavior=ft.ClipBehavior.HARD_EDGE,
    )

    page.add(
        ft.Column([
            stack,
            coord_text,
        ])
    )

    # 初期座標を表示
    update_coord_text()


ft.app(target=main)
```

画像の上に矩形を表示し、その矩形の角を自由に動かすことで、画像の任意の領域を選択できるようにしたい。
現状は四隅にハンドルが表示されるが、操作できるのは右下のみ。
完全に動作するようにして、コード全体を出力して。