tenho um script groovy para Freeplane. Ele tem um painel de pesquisa. Quero introduzir algum indicador de que a pesquisa finalizou. Não sei de qual modo faria essa indicação para o usuário sem criar um popup que quebre o flow de uso da interface. Pensei em incluir no final da lista de resultados uma linha assim "----search finished----". 

Atualmente, ao finalizar, ele printa println "Found ${resultCount} results.". 


Está assim:

    //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Quick Search Panel ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓


    quickSearchPanel = new JPanel(new BorderLayout()) {
        protected void paintComponent(Graphics g) {
            g.setColor(getBackground())
            g.fillRect(0, 0, getWidth(), getHeight())
            super.paintComponent(g)
        }
    }
    quickSearchPanel.setOpaque(false)
    quickSearchPanel.setBackground(new Color(0, 0, 0, 0))

    if (rtlOrientation) {
        quickSearchPanel.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT)
    } else {
        quickSearchPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT)
    }

//    int quickSearchPanelHeight = 130
//    quickSearchPanel.setBounds(0, recentSelectedNodesPanelHeight + 170, recentSelectedNodesPanelWidth, quickSearchPanelHeight)


//    JComboBox<String> searchField = new JComboBox<>(savedSearchCriteria.toArray(new String[0]))
    searchField = new JComboBox<>(savedSearchCriteria.toArray(new String[0]))

    searchField.setEditable(true)
    searchField.setSelectedItem("")

    searchEditor = (JTextField) searchField.getEditor().getEditorComponent()

    searchEditor.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "clearSearch")
    searchEditor.getActionMap().put("clearSearch", new AbstractAction() {
        @Override
        void actionPerformed(ActionEvent e) {
//            searchField.setSelectedItem("")
//            quickSearchResults.clear()
//            Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
//            Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
            clearQuickSearch()
            retractMasterPanel()
        }
    })


    searchEditor.getDocument().addDocumentListener(new DocumentListener() {
        @Override
        public void insertUpdate(DocumentEvent e) {
            scheduleLiveSearch()

            parentPanel.revalidate()
            parentPanel.repaint()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            scheduleLiveSearch()
            parentPanel.revalidate()
            parentPanel.repaint()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            scheduleLiveSearch()
            parentPanel.revalidate()
            parentPanel.repaint()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
        }

        private void scheduleLiveSearch() {
            liveSearchTimer.stop()
            liveSearchTimer.start()
        }
    })

    liveSearchTimer.addActionListener(new ActionListener() {
        @Override
        void actionPerformed(ActionEvent e) {
            searchText = searchEditor.getText().trim()

            if (!searchText.equals(lastSearchText)) {
                lastSearchText = searchText
                refreshList(searchText)
            }

            parentPanel.revalidate()
            parentPanel.repaint()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
            Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
        }

        private void refreshList(String searchText) {
            quickSearchResults.clear()
            refreshHighlighterCache()

            if (!searchText.isEmpty()) {
//                expandMasterPanel()
                final NodeModel rootNode = Controller.currentController.getSelection().selectionRoot

                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                    int resultCount = 0

                    @Override
                    protected Void doInBackground() throws Exception {
                        searchNodesRecursively(rootNode, searchText)
                        return null
                    }

                    @Override
                    protected void done() {
                        SwingUtilities.invokeLater({ ->
                            ensureOverlayExistsAndRepaint()
                            println "Found ${resultCount} results."
//                            quickSearchResults.addElement(node.delegate)
                            parentPanel.revalidate()
                            parentPanel.repaint()
                            Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
                            Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
                        })
                    }


                    private void searchNodesRecursively(NodeModel node, String searchText2) {
                        if (resultCount >= maxNumberOfResults) return;
//                        if (resultCount >= 500) return;

                        String nodeText = (node.text ?: "").toLowerCase();
                        String[] terms = searchText2.toLowerCase().split("\\s+");

                        List<String> foundDirect = [];
                        for (String term : terms) {
                            if (nodeText.contains(term)) {
                                foundDirect.add(term);
                            }
                        }

                        boolean nodeMatches = false;
                        if (!foundDirect.isEmpty()) {
                            nodeMatches = true;
                            for (String term : terms) {
                                if (!foundDirect.contains(term)) {
                                    boolean foundInAncestors = false;
                                    NodeModel ancestor = node.parent;
                                    while (ancestor != null && !foundInAncestors) {
                                        String ancestorText = (ancestor.text ?: "").toLowerCase();
                                        if (ancestorText.contains(term)) {
                                            foundInAncestors = true;
                                        }
                                        ancestor = ancestor.parent;
                                    }
                                    if (!foundInAncestors) {
                                        nodeMatches = false;
                                        break;
                                    }
                                }
                            }
                        }

                        if (nodeMatches) {
                            if (resultCount < maxNumberOfResults) {
                                resultCount++;
                                SwingUtilities.invokeLater({ -> quickSearchResults.addElement(node)
                                })
                            }
                        }

                        if (resultCount >= maxNumberOfResults) return;

                        for (child in node.children) {
                            if (resultCount >= maxNumberOfResults) break;
                            searchNodesRecursively(child, searchText2);
                        }
                    }



                }
                worker.execute()

                if (!savedSearchCriteria.contains(searchText)) {
                    savedSearchCriteria.add(0, searchText)
                } else {
                    savedSearchCriteria.remove(searchText)
                    savedSearchCriteria.add(0, searchText)
                }

                while (savedSearchCriteria.size() > 200) {
                    savedSearchCriteria.remove(savedSearchCriteria.size() - 1)
                }

                saveSettings()

                int caretPosition = searchEditor.getCaretPosition()

                searchField.removeAllItems()
                savedSearchCriteria.each { term ->
                    searchField.addItem(term)
                }

                searchEditor.setText(searchText)

                if (!searchField.isPopupVisible()) {
                    searchEditor.setCaretPosition(Math.min(caretPosition, searchText.length()))
                }
                parentPanel.revalidate()
                parentPanel.repaint()
                Controller.getCurrentController().getMapViewManager().getMapViewComponent().revalidate()
                Controller.getCurrentController().getMapViewManager().getMapViewComponent().repaint()
            }

        }
    })



    JButton clearButton = new JButton("X")
    clearButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            clearQuickSearch()
            retractMasterPanel()
//            updateAllGUIs()
        }
    })

    clearButton.setPreferredSize(new Dimension(widthOfTheClearButtonOnQuickSearchPanel, 1))
    clearButton.setForeground(Color.BLACK)
    clearButton.setBackground(Color.WHITE)
    clearButton.setBorder(BorderFactory.createEtchedBorder())
    clearButton.setOpaque(true)
    clearButton.setBorderPainted(true)
    clearButton.setFocusPainted(false)

    JPanel panelForSearchBox = new JPanel(new BorderLayout()) {
        {
            if (rtlOrientation) {
                setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT)
            } else {
                setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT)
            }
        }
    }

    panelForSearchBox.add(searchField, BorderLayout.CENTER)
    panelForSearchBox.add(clearButton, BorderLayout.EAST)


    panelForSearchBox.setOpaque(false)
    panelForSearchBox.setBackground(new Color(0, 0, 0, 0))


    quickSearchPanel.add(panelForSearchBox, BorderLayout.NORTH)

    innerPanelInQuickSearchPanel = new JPanel(new BorderLayout())

    innerPanelInQuickSearchPanel.setOpaque(false)
    innerPanelInQuickSearchPanel.setBackground(new Color(0, 0, 0, 0))

    quickSearchPanel.add(innerPanelInQuickSearchPanel, BorderLayout.CENTER)


    searchField.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseEntered(MouseEvent e) {
            isMouseOverSearchBox = true
        }

        @Override
        public void mouseExited(MouseEvent e) {
            isMouseOverSearchBox = false
        }
    })



    panelForSearchBox.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseEntered(MouseEvent e) {
            isMouseOverSearchBox = true
        }

        @Override
        public void mouseExited(MouseEvent e) {
            isMouseOverSearchBox = false
        }
    })


    searchEditor.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseEntered(MouseEvent e) {
            isMouseOverSearchBox = true
        }

        @Override
        public void mouseExited(MouseEvent e) {
            isMouseOverSearchBox = false
        }
    })


    clearButton.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseEntered(MouseEvent e) {
            isMouseOverSearchBox = true
        }

        @Override
        public void mouseExited(MouseEvent e) {
            isMouseOverSearchBox = false
        }
    })

    addQuickSearchShortcut(searchField)


    //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Quick Search Panel ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑