JJ_MKVMaker/lib/ui/browser/browser_toolbar.dart

212 lines
9.4 KiB
Dart

import 'package:flutter/material.dart';
import '../../app/app_controller.dart';
import '../../app/i18n_controller.dart' show I18nController;
import '../../l10n/tr.dart';
import '../app_actions.dart';
import '../key_hint.dart';
import '../theme.dart';
import '../work_panel.dart';
import 'web_nav.dart';
/// 브라우저 위쪽 막대 (앱 화면 이동 · 웹 페이지 이동 · 주소 칸 · 번역 · 다운로드 · Tor · 외부 · 쿠키 · 작업 현황 · 즐겨찾기).
/// 약속: 화면 상태를 보이기만 하고, 누르면 받은 함수를 부른다. 여기서 좌우로 밀면 화면 이동
/// (웹 페이지 위에서는 웹 페이지의 스크롤과 다투어 위 · 아래가 이상했음)
class BrowserToolbar extends StatelessWidget {
final AppController c;
final WebNav? nav;
final String url;
final double progress;
final bool canBack, canForward, starred, translating, likelyVideo, canDownload, workOpen, panelOpen;
final String downloadTip;
final TextEditingController address;
final FocusNode addressFocus;
final void Function(String input) onGo;
final VoidCallback onStar, onToggleTranslate, onDownload, onTor, onExternal, onClearData, onToggleWork, onTogglePanel;
const BrowserToolbar({
super.key,
required this.c,
required this.nav,
required this.url,
required this.progress,
required this.canBack,
required this.canForward,
required this.starred,
required this.translating,
required this.likelyVideo,
required this.canDownload,
required this.downloadTip,
required this.workOpen,
required this.panelOpen,
required this.address,
required this.addressFocus,
required this.onGo,
required this.onStar,
required this.onToggleTranslate,
required this.onDownload,
required this.onTor,
required this.onExternal,
required this.onClearData,
required this.onToggleWork,
required this.onTogglePanel,
});
@override
Widget build(BuildContext context) => SwipeNav(current: 'browser', anywhere: true, child: _bar());
Widget _bar() {
IconButton btn(IconData i, String tip, VoidCallback? f, {Color? color}) => IconButton(
tooltip: tip,
visualDensity: VisualDensity.compact,
icon: Icon(i, size: 20, color: color),
onPressed: f,
);
final download = canDownload ? onDownload : null;
final home = c.settings.browser.homeUrl;
// 주소 칸이 좁아지면 (폰 세로 · 태블릿 세로 · 폰 가로 100%) 자주 안 쓰는 버튼은 ⋮ 메뉴로 - 주소가 "http" 만 보이거나
// 아예 사라지던 것. 버튼을 없애지는 않는다.
// 201: 폰 가로처럼 넓어도 버튼이 폭을 다 차지하면 주소 칸이 사라졌다 - 그때는 두 줄 (아래 줄을 주소 칸이 다 씀)
return AppTopBar(
minMiddleWidth: 380,
nav: const AppNavButtons(onBrowserPage: true),
actions: AppActions(c: c),
middle: LayoutBuilder(builder: (context, box) {
final compact = box.maxWidth < 640;
return Row(children: [
if (!compact) ...[
Container(width: 1, height: 24, color: JjColors.border),
const SizedBox(width: 4),
],
// 웹 페이지 이동 (앱 화면 이동인 왼쪽 버튼과 구분되는 모양)
btn(Icons.arrow_back_ios_new, keyHint(tr('이전 페이지'), 'Alt+←'), canBack ? () => nav?.back() : null),
btn(Icons.arrow_forward_ios, keyHint(tr('다음 페이지'), 'Alt+→'), canForward ? () => nav?.forward() : null),
progress < 1
? btn(Icons.close, tr('중지'), () => nav?.stop())
: btn(Icons.refresh, keyHint(tr('새로고침'), 'F5'), () => nav?.reload()),
if (!compact) btn(Icons.home_outlined, tr('홈'), () => onGo(home)),
const SizedBox(width: 6),
Expanded(
child: TextField(
controller: address,
focusNode: addressFocus,
style: const TextStyle(fontSize: 13),
decoration: InputDecoration(
isDense: true,
filled: true,
fillColor: JjColors.bg,
hintText: keyHint(tr('주소 입력 또는 검색'), 'Ctrl+L'),
prefixIcon: Icon(url.startsWith('https') ? Icons.lock_outline : Icons.public, size: 16),
prefixIconConstraints: const BoxConstraints(minWidth: 32),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(18), borderSide: BorderSide.none),
suffixIcon: IconButton(
tooltip: starred ? keyHint(tr('즐겨찾기 수정'), 'Ctrl+D') : keyHint(tr('즐겨찾기 추가'), 'Ctrl+D'),
icon: Icon(starred ? Icons.star : Icons.star_border, size: 18,
color: starred ? JjColors.amber : JjColors.textDim),
onPressed: onStar,
),
),
onSubmitted: onGo,
),
),
const SizedBox(width: 4),
if (!compact)
btn(
Icons.translate,
translating
? tr('원문 보기 (번역 끄기)')
: trf('이 페이지를 {0}(으)로 번역', [I18nController.nativeName(c.settings.display.language)]),
onToggleTranslate,
color: translating ? JjColors.accent : null),
const SizedBox(width: 4),
if (compact)
likelyVideo
? IconButton.filled(
tooltip: downloadTip,
visualDensity: VisualDensity.compact,
onPressed: download,
icon: const Icon(Icons.download, size: 18),
)
: IconButton.outlined(
tooltip: downloadTip,
visualDensity: VisualDensity.compact,
onPressed: download,
icon: const Icon(Icons.download, size: 18),
)
else
Tooltip(
message: downloadTip,
child: likelyVideo
? FilledButton.icon(
onPressed: download,
icon: const Icon(Icons.download, size: 18),
label: Text(tr('다운로드')),
)
: OutlinedButton.icon(
onPressed: download,
icon: const Icon(Icons.download, size: 18),
label: Text(tr('다운로드')),
),
),
const SizedBox(width: 4),
if (!compact) btn(Icons.vpn_lock_outlined, tr('Tor 브라우저'), onTor),
if (!compact) btn(Icons.open_in_new, tr('외부 브라우저로 열기'), onExternal),
if (!compact) btn(Icons.cookie_outlined, tr('쿠키 · 방문 기록 지우기'), onClearData),
if (c.busy && !workOpen && !compact)
InkWell(
onTap: onToggleWork,
borderRadius: BorderRadius.circular(6),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
child: JobIndicator(c: c, compact: true),
),
),
if (!compact)
btn(workOpen ? Icons.view_sidebar : Icons.view_sidebar_outlined, keyHint(tr('작업 현황 보기 · 화면 분할'), 'Ctrl+Shift+J'),
onToggleWork, color: c.busy ? JjColors.accent : null),
if (!compact)
btn(panelOpen ? Icons.bookmarks : Icons.bookmarks_outlined, keyHint(tr('즐겨찾기 관리'), 'Ctrl+Shift+B'), onTogglePanel)
else
PopupMenuButton<String>(
tooltip: tr('더 보기'),
icon: const Icon(Icons.more_vert, size: 20),
onSelected: (v) => switch (v) {
'home' => onGo(home),
'translate' => onToggleTranslate(),
'external' => onExternal(),
'work' => onToggleWork(),
'clear' => onClearData(),
'tor' => onTor(),
_ => onTogglePanel(),
},
itemBuilder: (_) => [
PopupMenuItem(value: 'home', child: ListTile(dense: true, leading: const Icon(Icons.home_outlined), title: Text(tr('홈')))),
PopupMenuItem(
value: 'translate',
child: ListTile(
dense: true,
leading: Icon(Icons.translate, color: translating ? JjColors.accent : null),
title: Text(translating ? tr('원문 보기 (번역 끄기)') : tr('이 페이지 번역')))),
PopupMenuItem(
value: 'bookmarks',
child: ListTile(dense: true, leading: const Icon(Icons.bookmarks_outlined), title: Text(tr('즐겨찾기 관리')))),
PopupMenuItem(
value: 'work',
child: ListTile(dense: true, leading: const Icon(Icons.view_sidebar_outlined), title: Text(tr('작업 현황')))),
PopupMenuItem(
value: 'tor',
child: ListTile(dense: true, leading: const Icon(Icons.vpn_lock_outlined), title: Text(tr('Tor 브라우저')))),
PopupMenuItem(
value: 'external',
child: ListTile(dense: true, leading: const Icon(Icons.open_in_new), title: Text(tr('외부 브라우저로 열기')))),
PopupMenuItem(
value: 'clear',
child: ListTile(dense: true, leading: const Icon(Icons.cookie_outlined), title: Text(tr('쿠키 · 방문 기록 지우기')))),
],
),
]);
}),
);
}
}