JJ_MKVMaker/lib/ui/browser/bookmark_bar.dart

159 lines
6.8 KiB
Dart

import 'package:flutter/material.dart';
import '../../app/bookmarks_controller.dart';
import '../../core/bookmarks.dart';
import '../../l10n/tr.dart';
import '../bookmark_ui.dart';
import '../theme.dart';
// ───────── 즐겨찾기 표시줄 (주소창 아래) ─────────
/// 약속: 즐겨찾기 표시줄의 항목을 보이고, 누르면 [onOpen] · [onOpenExternal] · [onOpenManager] 를 부른다 (바꾸기는 [bm] 이)
class BookmarkBar extends StatelessWidget {
final BookmarksController bm;
final void Function(String url) onOpen;
final void Function(String url) onOpenExternal;
final String currentUrl;
final String currentTitle;
final void Function({String? folderId}) onOpenManager;
const BookmarkBar({
super.key,
required this.bm,
required this.onOpen,
required this.onOpenExternal,
this.currentUrl = '',
this.currentTitle = '',
required this.onOpenManager,
});
void _menu(BuildContext context, Offset at, [BookmarkNode? n]) => showBookmarkMenu(context,
bm: bm,
globalPosition: at,
n: n,
folderId: BookmarkTree.barId,
onOpen: onOpen,
onOpenExternal: onOpenExternal,
currentUrl: currentUrl,
currentTitle: currentTitle,
onOpenManager: () => onOpenManager(folderId: n != null && n.isFolder ? n.id : null));
@override
Widget build(BuildContext context) {
final items = bm.tree.bar.children!;
// 빈 곳: 오른쪽 클릭 메뉴 (페이지 · 폴더 추가, 관리자), 다른 폴더의 즐겨찾기를 끌어다 놓으면 표시줄 맨 뒤로
return DragTarget<BookmarkDrag>(
// 다른 폴더에서 온 것만 (표시줄 안에서는 다른 즐겨찾기 위에 놓아 순서를 바꾼다. 길게 눌렀다 떼도 맨 뒤로 가지 않게)
onWillAcceptWithDetails: (d) =>
bm.canMoveInto(d.data.id, BookmarkTree.barId) && bm.tree.parentOf(d.data.id)?.id != BookmarkTree.barId,
onAcceptWithDetails: (d) => bm.moveInto(d.data.id, BookmarkTree.barId),
builder: (context, _, _) => GestureDetector(
behavior: HitTestBehavior.opaque,
onSecondaryTapDown: (d) => _menu(context, d.globalPosition),
// 터치 화면: 길게 누르면 같은 메뉴
onLongPressStart: (d) => _menu(context, d.globalPosition),
child: Container(
height: 32,
color: JjColors.panel,
padding: const EdgeInsets.symmetric(horizontal: 6),
child: items.isEmpty
? Align(
alignment: Alignment.centerLeft,
child: Text(tr(' ☆ 를 누르면 여기에 즐겨찾기가 추가됩니다 · 오른쪽 클릭: 폴더 추가'),
style: TextStyle(fontSize: 11, color: JjColors.textDim)),
)
: ListView(
scrollDirection: Axis.horizontal,
children: [for (final (i, n) in items.indexed) _barItem(context, n, i)],
),
),
),
);
}
Widget _barItem(BuildContext context, BookmarkNode n, int index) {
final label = n.title.length > 24 ? '${n.title.substring(0, 23)}…' : n.title;
final Widget button;
if (n.isFolder) {
// 폴더: 누르면 내용 (하위 폴더는 옆으로 펼침), 즐겨찾기를 끌어다 놓으면 그 안으로
button = BookmarkFolderDrop(
bm: bm,
folderId: n.id,
child: MenuAnchor(
menuChildren: _folderMenu(context, n),
builder: (context, ctl, _) => TextButton.icon(
onPressed: () => ctl.isOpen ? ctl.close() : ctl.open(),
icon: Icon(Icons.folder, size: 15, color: JjColors.amber),
label: Text(label, style: const TextStyle(fontSize: 12)),
),
),
);
} else {
// 주소: 다른 즐겨찾기를 이 위에 놓으면 그 자리 (앞) 로 옮긴다
button = DragTarget<BookmarkDrag>(
onWillAcceptWithDetails: (d) => d.data.id != n.id && bm.canMoveInto(d.data.id, BookmarkTree.barId),
onAcceptWithDetails: (d) => bm.move(d.data.id, BookmarkTree.barId, index),
builder: (context, cand, _) => Container(
decoration: BoxDecoration(
border: Border(left: BorderSide(color: cand.isNotEmpty ? JjColors.accent : Colors.transparent, width: 2)),
),
child: Tooltip(
message: '${n.title}\n${n.url}',
waitDuration: const Duration(milliseconds: 600),
// 길게 누르기는 메뉴에 (마우스를 올리면 주소)
triggerMode: TooltipTriggerMode.manual,
child: TextButton.icon(
onPressed: () => onOpen(n.url!),
icon: const Icon(Icons.public, size: 14),
label: Text(label, style: const TextStyle(fontSize: 12)),
),
),
),
);
}
return GestureDetector(
onSecondaryTapDown: (d) => _menu(context, d.globalPosition, n),
// 길게 누르기 (터치 화면): 움직이지 않고 떼면 메뉴, 끌면 옮기기 (BookmarkDraggable)
child: BookmarkDraggable(n: n, onMenu: (at) => _menu(context, at, n), child: button),
);
}
List<Widget> _folderMenu(BuildContext context, BookmarkNode f) => [
if (f.children!.isEmpty) MenuItemButton(child: Text(tr('(비어 있음)'))),
for (final c in f.children!)
c.isFolder
? SubmenuButton(
leadingIcon: Icon(Icons.folder, size: 16, color: JjColors.amber),
menuChildren: _folderMenu(context, c),
child: Text(c.title),
)
: MenuItemButton(
leadingIcon: const Icon(Icons.public, size: 16),
onPressed: () => onOpen(c.url!),
child: Text(c.title),
),
const Divider(height: 8),
MenuItemButton(
leadingIcon: const Icon(Icons.bookmark_add_outlined, size: 16),
onPressed: currentUrl.startsWith('http')
? () => bm.addLink(currentTitle.isEmpty ? currentUrl : currentTitle, currentUrl, parentId: f.id)
: null,
child: Text(tr('이 폴더에 현재 페이지 추가')),
),
MenuItemButton(
leadingIcon: const Icon(Icons.create_new_folder_outlined, size: 16),
onPressed: () => showNewFolderDialog(context, bm, f.id),
child: Text(tr('이 폴더에 새 폴더')),
),
MenuItemButton(
leadingIcon: const Icon(Icons.edit_outlined, size: 16),
onPressed: () => showBookmarkEditor(context, bm, f),
child: Text(tr('폴더 이름 바꾸기 · 이동')),
),
MenuItemButton(
leadingIcon: const Icon(Icons.bookmarks_outlined, size: 16),
onPressed: () => onOpenManager(folderId: f.id),
child: Text(tr('즐겨찾기 관리자에서 보기')),
),
];
}