@@ -9,10 +9,12 @@ import 'package:client/services/sessions/session_sql_result.dart';
99import 'package:client/services/sessions/session_conn.dart' ;
1010import 'package:client/services/sessions/sessions.dart' ;
1111import 'package:client/widgets/const.dart' ;
12+ import 'package:client/widgets/menu.dart' ;
1213import 'package:client/widgets/dialog.dart' ;
1314import 'package:client/widgets/button.dart' ;
1415import 'package:client/widgets/divider.dart' ;
1516import 'package:client/widgets/loading.dart' ;
17+ import 'package:client/widgets/tooltip.dart' ;
1618import 'package:flutter/material.dart' ;
1719import 'package:sql_parser/parser.dart' ;
1820import 'package:hugeicons/hugeicons.dart' ;
@@ -293,78 +295,161 @@ class SchemaBar extends ConsumerStatefulWidget {
293295
294296class _SchemaBarState extends ConsumerState <SchemaBar > {
295297 bool isEnter = false ;
298+ List <String >? _schemas;
299+ late final TextEditingController _schemaSearchController;
300+
301+ @override
302+ void initState () {
303+ super .initState ();
304+ _schemaSearchController = TextEditingController ();
305+ }
306+
307+ @override
308+ void dispose () {
309+ _schemaSearchController.dispose ();
310+ super .dispose ();
311+ }
312+
313+ @override
314+ void didUpdateWidget (covariant SchemaBar oldWidget) {
315+ super .didUpdateWidget (oldWidget);
316+ if (oldWidget.instanceId != widget.instanceId) {
317+ _schemas = null ;
318+ _schemaSearchController.clear ();
319+ }
320+ }
321+
322+ void _onSchemaSearchChanged () {
323+ setState (() {});
324+ }
325+
326+ List <String > _filteredSchemas (List <String > schemas, String searchText) {
327+ if (searchText.isEmpty) return schemas;
328+ return schemas.where ((s) => s.toLowerCase ().contains (searchText.toLowerCase ())).toList ();
329+ }
330+
331+ Future <void > _fetchSchemas () async {
332+ if (widget.disable || widget.instanceId == null ) return ;
333+ if (_schemas != null ) return ;
334+ final schemas = await ref.read (instancesServicesProvider.notifier).getSchemas (widget.instanceId! );
335+ if (mounted) {
336+ setState (() => _schemas = schemas);
337+ }
338+ }
296339
297340 @override
298341 Widget build (BuildContext context) {
299342 final color = (isEnter && ! widget.disable)
300343 ? Theme .of (context).colorScheme.primary // schema 鼠标移入的颜色
301344 : Theme .of (context).colorScheme.onSurface;
302- return Padding (
345+
346+ final schemaBarContent = Padding (
303347 padding: const EdgeInsets .symmetric (horizontal: 5 ),
304348 child: MouseRegion (
305- onEnter: (_) {
306- setState (() {
307- isEnter = true ;
308- });
309- },
310- onExit: (_) {
311- setState (() {
312- isEnter = false ;
313- });
314- },
315- child: GestureDetector (
316- onTapUp: (detail) async {
317- if (widget.disable) {
318- return ;
319- }
320- final position = detail.globalPosition;
321- final RenderBox overlay = Overlay .of (context).context.findRenderObject () as RenderBox ;
322- final overlayPos = overlay.localToGlobal (Offset .zero);
323-
324- List <String >? schemas = await ref.read (instancesServicesProvider.notifier).getSchemas (widget.instanceId! );
325-
326- // todo
327- showMenu (
328- context: context,
329- position: RelativeRect .fromLTRB (
330- position.dx - overlayPos.dx,
331- position.dy - overlayPos.dy,
332- position.dx - overlayPos.dx,
333- position.dy - overlayPos.dy,
334- ),
335- items: schemas.map ((schema) {
336- return PopupMenuItem <String >(
337- height: 30 ,
338- onTap: () async {
339- await ref.read (sessionConnsServicesProvider.notifier).setCurrentSchema (widget.connId! , schema);
340- },
341- child: Text (schema, overflow: TextOverflow .ellipsis));
342- }).toList ());
343- },
349+ onEnter: (_) => setState (() => isEnter = true ),
350+ onExit: (_) => setState (() => isEnter = false ),
351+ child: Listener (
352+ onPointerDown: (_) => _fetchSchemas (),
344353 child: Container (
345- padding: const EdgeInsets .fromLTRB (0 , kSpacingTiny, 0 , kSpacingTiny),
346- child: Row (
347- children: [
348- HugeIcon (
349- icon: HugeIcons .strokeRoundedDatabase,
350- color: color,
351- size: kIconSizeSmall,
354+ padding: const EdgeInsets .fromLTRB (0 , kSpacingTiny, 0 , kSpacingTiny),
355+ child: Row (
356+ children: [
357+ HugeIcon (
358+ icon: HugeIcons .strokeRoundedDatabase,
359+ color: color,
360+ size: kIconSizeSmall,
361+ ),
362+ Container (
363+ padding: const EdgeInsets .only (left: kSpacingTiny),
364+ width: 120 ,
365+ child: Align (
366+ alignment: Alignment .centerLeft,
367+ child: Text (
368+ widget.currentSchema ?? "" ,
369+ overflow: TextOverflow .ellipsis,
370+ style: TextStyle (color: color),
371+ ),
372+ ),
373+ ),
374+ ],
375+ ),
376+ ),
377+ ),
378+ ),
379+ );
380+
381+ final filteredSchemas = _schemas == null ? null : _filteredSchemas (_schemas! , _schemaSearchController.text);
382+
383+ final tabs = filteredSchemas == null
384+ ? [
385+ OverlayMenuItem (
386+ height: 36 ,
387+ child: const Center (child: Loading .medium ()),
388+ ),
389+ ]
390+ : filteredSchemas.map ((schema) {
391+ final isSelected = schema == widget.currentSchema;
392+ final color = isSelected ? Theme .of (context).colorScheme.primary : Theme .of (context).colorScheme.onSurface;
393+ return OverlayMenuItem (
394+ height: 30 ,
395+ child: Padding (
396+ padding: const EdgeInsets .symmetric (horizontal: kSpacingSmall),
397+ child: Align (
398+ alignment: Alignment .centerLeft,
399+ child: Row (
400+ children: [
401+ HugeIcon (
402+ icon: HugeIcons .strokeRoundedDatabase,
403+ color: color,
404+ size: kIconSizeSmall,
405+ ),
406+ const SizedBox (width: kSpacingTiny),
407+ Expanded (
408+ child: TooltipText (text: schema, style: TextStyle (color: color)),
409+ ),
410+ ],
352411 ),
353- Container (
354- padding: const EdgeInsets .only (left: kSpacingTiny),
355- width: 120 ,
356- child: Align (
357- alignment: Alignment .centerLeft,
358- child: Text (
359- widget.currentSchema ?? "" ,
360- overflow: TextOverflow .ellipsis,
361- style: TextStyle (color: color),
362- ))),
363- ],
364- )),
412+ ),
413+ ),
414+ onTabSelected: () async {
415+ await ref.read (sessionConnsServicesProvider.notifier).setCurrentSchema (widget.connId! , schema);
416+ },
417+ );
418+ }).toList ();
419+
420+ if (widget.disable) {
421+ return schemaBarContent;
422+ }
423+
424+ final header = OverlayMenuHeader (
425+ height: 36 ,
426+ child: Padding (
427+ padding: const EdgeInsets .fromLTRB (kSpacingSmall, kSpacingTiny, kSpacingSmall, kSpacingTiny),
428+ child: SearchBarTheme (
429+ data: SearchBarThemeData (
430+ textStyle: WidgetStatePropertyAll (Theme .of (context).textTheme.bodySmall),
431+ backgroundColor: WidgetStatePropertyAll (Theme .of (context).colorScheme.surfaceContainer),
432+ elevation: const WidgetStatePropertyAll (0 ),
433+ constraints: const BoxConstraints (minHeight: 24 ),
434+ ),
435+ child: SearchBar (
436+ controller: _schemaSearchController,
437+ onChanged: (_) => _onSchemaSearchChanged (),
438+ trailing: const [Icon (Icons .search, size: kIconSizeSmall)],
439+ ),
365440 ),
366441 ),
367442 );
443+
444+ return OverlayMenu (
445+ spacing: kSpacingTiny,
446+ maxHeight: 300 ,
447+ maxWidth: 300 ,
448+ tabs: tabs,
449+ header: header,
450+ footer: OverlayMenuFooter (height: kSpacingSmall, child: SizedBox ()),
451+ child: schemaBarContent,
452+ );
368453 }
369454}
370455
0 commit comments