159 lines
6.8 KiB
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('즐겨찾기 관리자에서 보기')),
|
|
),
|
|
];
|
|
}
|