212 lines
9.4 KiB
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('쿠키 · 방문 기록 지우기')))),
|
|
],
|
|
),
|
|
]);
|
|
}),
|
|
);
|
|
}
|
|
}
|