Skip to content

Conversation

@Minion3665
Copy link
Member

This change came about because of the difficulty of finding items in really long dropdown lists. We can make a fairly quick solution to this using the existing filtering functions, tied to an input element which sticks to the top of the dropdown list...

This probably shouldn't be merged as is, in particular:

  • I'm unconvinced by using position: sticky, since as overscroll will cause the search bar to move (which is cosmetically weird, though not actually a functional problem)
  • We need to figure out what to do about the filter popup, which now has both a "Search items..." and a "Search..." box. I prefer my search box and would propose to remove "Search items..." (but I guess it's a core change)

Nevertheless, hopefully it'll be useful for you to test!

Change-Id: Ib8000bc3e5566993fa6eea89adcc617b6a6a6964

  • Resolves: #
  • Target version: master

Summary

TODO

  • ...

Checklist

  • I have run make prettier-write and formatted the code.
  • All commits have Change-Id
  • I have run tests with make check
  • I have issued make run and manually verified that everything looks okay
  • Documentation (manuals or wiki) has been updated or is not required

@github-project-automation github-project-automation bot moved this to To Review in Collabora Online Dec 5, 2025
@Minion3665 Minion3665 marked this pull request as draft December 5, 2025 14:18
@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch from 5f29930 to e093a48 Compare December 5, 2025 14:26
@pedropintosilva
Copy link
Contributor

Skylers-new-search-calc-2025-12-15_11.33.49.webm
Screenshot_20251215_113241 Screenshot_20251215_113156

@Minion3665
Copy link
Member Author

duplicate search field in checklists is fixed by https://gerrit.libreoffice.org/c/core/+/195719

@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch from e093a48 to a36d0bf Compare December 16, 2025 16:18
@Minion3665 Minion3665 marked this pull request as ready for review December 16, 2025 16:55
@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch 3 times, most recently from fdced34 to 0b01557 Compare December 17, 2025 11:36
@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch from 0b01557 to 7c9696d Compare December 18, 2025 17:31
@eszkadev eszkadev added the draft label Dec 30, 2025
@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch from 7c9696d to 46e7afd Compare January 5, 2026 13:13
@Minion3665 Minion3665 requested a review from eszkadev January 5, 2026 13:15
@Minion3665
Copy link
Member Author

Range diff:

1:  7c9696de43 ! 1:  46e7afdbfe feat(treeview): add a search field to lists
    @@ browser/css/jsdialogs.css: algned to the bottom */
     +  padding-right: 1px;
     +}
     +
    -+#ui-treeview-search-input {
    ++[id^='ui-treeview-search-input'] {
     +  width: 100%;
     +  box-sizing: border-box;
     +}
     +
    - .modalpopup .ui-treeview {
    + #__MENU__.ui-treeview {
        min-height: min-content;
      }

    + ## browser/src/canvas/sections/CommentListSection.ts ##
    +@@ browser/src/canvas/sections/CommentListSection.ts: export class CommentSection extends CanvasSectionObject {
    +               fireKeyEvents: true,
    +               hideIfEmpty: true,
    +               entries: [] as Array<TreeEntryJSON>,
    ++              noSearchField: true,
    +           },
    +           {
    +               id: '',
    +
    + ## browser/src/control/AutoCompletePopup.ts ##
    +@@ browser/src/control/AutoCompletePopup.ts: abstract class AutoCompletePopup {
    +           singleclickactivate: false,
    +           fireKeyEvents: true,
    +           entries: [] as Array<TreeEntryJSON>,
    ++          noSearchField: true,
    +       } as TreeWidgetJSON;
    +   }
    +
    +
    + ## browser/src/control/Control.QuickFindPanel.ts ##
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +       if (
    +           data.control.id === 'searchfinds' &&
    +           data.control.type === 'treelistbox'
    +-      )
    ++      ) {
    +           e.data.control.ignoreFocus = true;
    ++          e.data.control.noSearchField = true;
    ++      }
    +
    +       if (!super.onJSUpdate(e)) return false;
    +
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +           quickFindControls.classList.toggle('hidden', isEmpty);
    +   }
    +
    ++  removeSearchFields(quickFindData: any): any {
    ++      if (quickFindData.children) {
    ++          const modifiedData = JSON.parse(JSON.stringify(quickFindData));
    ++
    ++          const _removeSearchFields = (data: any) => {
    ++              for (const child of data.children) {
    ++                  if (child.type === 'treelistbox') {
    ++                      child.noSearchField = true;
    ++                  }
    ++
    ++                  if (child.children) {
    ++                      _removeSearchFields(child);
    ++                  }
    ++              }
    ++          };
    ++
    ++          _removeSearchFields(modifiedData);
    ++
    ++          return modifiedData;
    ++      }
    ++
    ++      return quickFindData;
    ++  }
    ++
    +   addPlaceholderIfEmpty(quickFindData: any): any {
    +       const hasEntries =
    +           quickFindData.children &&
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +
    +       this.container.innerHTML = '';
    +
    +-      const modifiedData = this.addPlaceholderIfEmpty(quickFindData);
    ++      }
    +
    +       if (!super.onJSUpdate(e)) return false;
    +
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +           quickFindControls.classList.toggle('hidden', isEmpty);
    +   }
    +
    ++  removeSearchFields(quickFindData: any): any {
    ++      if (quickFindData.children) {
    ++          const modifiedData = JSON.parse(JSON.stringify(quickFindData));
    ++
    ++          const _removeSearchFields = (data: any) => {
    ++              for (const child of data.children) {
    ++                  if (child.type === 'treelistbox') {
    ++                      child.noSearchField = true;
    ++                  }
    ++
    ++                  if (child.children) {
    ++                      _removeSearchFields(child);
    ++                  }
    ++              }
    ++          };
    ++
    ++          _removeSearchFields(modifiedData);
    ++
    ++          return modifiedData;
    ++      }
    ++
    ++      return quickFindData;
    ++  }
    ++
    +   addPlaceholderIfEmpty(quickFindData: any): any {
    +       const hasEntries =
    +           quickFindData.children &&
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +
    +       this.container.innerHTML = '';
    +
    +-      const modifiedData = this.addPlaceholderIfEmpty(quickFindData);
    -+#ui-treeview-search-input {
    ++[id^='ui-treeview-search-input'] {
     +  width: 100%;
     +  box-sizing: border-box;
     +}
     +
    - .modalpopup .ui-treeview {
    + #__MENU__.ui-treeview {
        min-height: min-content;
      }

    + ## browser/src/canvas/sections/CommentListSection.ts ##
    +@@ browser/src/canvas/sections/CommentListSection.ts: export class CommentSection extends CanvasSectionObject {
    +               fireKeyEvents: true,
    +               hideIfEmpty: true,
    +               entries: [] as Array<TreeEntryJSON>,
    ++              noSearchField: true,
    +           },
    +           {
    +               id: '',
    +
    + ## browser/src/control/AutoCompletePopup.ts ##
    +@@ browser/src/control/AutoCompletePopup.ts: abstract class AutoCompletePopup {
    +           singleclickactivate: false,
    +           fireKeyEvents: true,
    +           entries: [] as Array<TreeEntryJSON>,
    ++          noSearchField: true,
    +       } as TreeWidgetJSON;
    +   }
    +
    +
    + ## browser/src/control/Control.QuickFindPanel.ts ##
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +       if (
    +           data.control.id === 'searchfinds' &&
    +           data.control.type === 'treelistbox'
    +-      )
    ++      ) {
    +           e.data.control.ignoreFocus = true;
1:  7c9696de43 ! 1:  46e7afdbfe feat(treeview): add a search field to lists
    @@ browser/css/jsdialogs.css: algned to the bottom */
     +  padding-right: 1px;
     +}
     +
    -+#ui-treeview-search-input {
    ++[id^='ui-treeview-search-input'] {
     +  width: 100%;
     +  box-sizing: border-box;
     +}
     +
    - .modalpopup .ui-treeview {
    + #__MENU__.ui-treeview {
        min-height: min-content;
      }

    + ## browser/src/canvas/sections/CommentListSection.ts ##
    +@@ browser/src/canvas/sections/CommentListSection.ts: export class CommentSection extends CanvasSectionObject {
    +               fireKeyEvents: true,
    +               hideIfEmpty: true,
    +               entries: [] as Array<TreeEntryJSON>,
    ++              noSearchField: true,
    +           },
    +           {
    +               id: '',
    +
    + ## browser/src/control/AutoCompletePopup.ts ##
    +@@ browser/src/control/AutoCompletePopup.ts: abstract class AutoCompletePopup {
    +           singleclickactivate: false,
    +           fireKeyEvents: true,
    +           entries: [] as Array<TreeEntryJSON>,
    ++          noSearchField: true,
    +       } as TreeWidgetJSON;
    +   }
    +
    +
    + ## browser/src/control/Control.QuickFindPanel.ts ##
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +       if (
    +           data.control.id === 'searchfinds' &&
    +           data.control.type === 'treelistbox'
    +-      )
    ++      ) {
    +           e.data.control.ignoreFocus = true;
    ++          e.data.control.noSearchField = true;
    ++      }
    +
    +       if (!super.onJSUpdate(e)) return false;
    +
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +           quickFindControls.classList.toggle('hidden', isEmpty);
    +   }
    +
    ++  removeSearchFields(quickFindData: any): any {
    ++      if (quickFindData.children) {
    ++          const modifiedData = JSON.parse(JSON.stringify(quickFindData));
    ++
    ++          const _removeSearchFields = (data: any) => {
    ++              for (const child of data.children) {
    ++                  if (child.type === 'treelistbox') {
    ++                      child.noSearchField = true;
    ++                  }
    ++
    ++                  if (child.children) {
    ++                      _removeSearchFields(child);
    ++                  }
    ++              }
    ++          };
    ++
    ++          _removeSearchFields(modifiedData);
    ++
    ++          return modifiedData;
    ++      }
    ++
    ++      return quickFindData;
    ++  }
    ++
    +   addPlaceholderIfEmpty(quickFindData: any): any {
    +       const hasEntries =
    +           quickFindData.children &&
    +@@ browser/src/control/Control.QuickFindPanel.ts: class QuickFindPanel extends SidebarBase {
    +
    +       this.container.innerHTML = '';
    +
    +-      const modifiedData = this.addPlaceholderIfEmpty(quickFindData);
    ++      let modifiedData = this.removeSearchFields(quickFindData);
    ++      modifiedData = this.addPlaceholderIfEmpty(modifiedData);
    +
    +       this.builder?.build(this.container, [modifiedData], false);
    +
    +
    + ## browser/src/control/jsdialog/Definitions.Types.ts ##
    +@@ browser/src/control/jsdialog/Definitions.Types.ts: interface TreeWidgetJSON extends WidgetJSON {
    +   highlightTerm?: string; // what, if any, entries are we highlighting?
    +   ignoreFocus?: boolean; // When true, does't focus to selected item automatically.
    +   customEntryRenderer?: boolean;
    ++  noSearchField?: boolean; // When true, the widget shouldn't have a search field added
    + }
    +
    + interface IconViewEntry {
    +
      ## browser/src/control/jsdialog/Widget.TreeView.ts ##
     @@ browser/src/control/jsdialog/Widget.TreeView.ts: class TreeViewControl {
            if (level === 1 && !hasSelectedEntry) this.makeTreeViewFocusable(true);
    @@ browser/src/control/jsdialog/Widget.TreeView.ts: class TreeViewControl {

     +  showSearchBar(parent: HTMLElement) {
     +      const searchBox = document.createElement('input');
    -+      searchBox.id = 'ui-treeview-search-input'; // Form fields should have either a name or an ID - using this instead of a class
    ++      searchBox.id = JSDialog.MakeIdUnique('ui-treeview-search-input'); // Form fields should have either a name or an ID - using this instead of a class
     +      searchBox.placeholder = 'Search...';
     +      searchBox.addEventListener('input', () =>
     +          this.filterEntries(searchBox.value),
    @@ browser/src/control/jsdialog/Widget.TreeView.ts: class TreeViewControl {

     +      if (
     +          this._isListbox &&
    -+          this._container.id !== 'searchfinds' &&
    -+          this._container.id !== 'mentionPopupList'
    ++          !data.noSearchField
     +      ) {
     +          this.showSearchBar(this._container);
     +      }

@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch 2 times, most recently from 7e28adb to d6e5f4b Compare January 6, 2026 15:41
Copy link
Contributor

@eszkadev eszkadev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code looks ok

@github-project-automation github-project-automation bot moved this from To Review to To Test in Collabora Online Jan 6, 2026
@eszkadev eszkadev removed the draft label Jan 6, 2026
@Minion3665
Copy link
Member Author

redraft, I've noticed an issue with treeviews with different numbers of columns -- seems that _columns isn't always quite right and since as we dynamically figure this out we can't use infinity or -1 to just go to the last column either

@Minion3665 Minion3665 marked this pull request as draft January 6, 2026 16:03
@eszkadev
Copy link
Contributor

eszkadev commented Jan 6, 2026

there are icons and expanders which might occupy some place in some tables I think

@Minion3665
Copy link
Member Author

there are icons and expanders which might occupy some place in some tables I think

yeah, I'm going to move the search bar creation to after we fill columns, and count up what the largest column we get there is...

@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch from d6e5f4b to 514e3cf Compare January 6, 2026 16:16
This change came about because of the difficulty of finding items in
really long dropdown lists. We can make a fairly quick solution to this
using the existing filtering functions, tied to an input element which
sticks to the top of the dropdown list...

I'm a little unconvinced by using position: sticky, since as overscroll
will cause the search bar to move (which is cosmetically weird, though
not actually a functional problem). Nevertheless, I couldn't find
something obviously better without making a risky change to treeviews at
large. I suspect you can do some position: absolute thing to minimize
trouble, but whatever it is it won't be clean.

This depends on https://gerrit.libreoffice.org/c/core/+/195719 to hide
the search field with checkbox lists to avoid duplicates there

We also explicitly ignore some dropdown lists - the search results and
the mention popup - since as having a search there doesn't make a lot of
sense...

Signed-off-by: Skyler Grey <[email protected]>
Change-Id: Ib8000bc3e5566993fa6eea89adcc617b6a6a6964
@Minion3665 Minion3665 force-pushed the private/minion/push-orzzzonwluut branch from 514e3cf to 5df6c0c Compare January 6, 2026 16:18
@Minion3665
Copy link
Member Author

fixed

@Minion3665 Minion3665 marked this pull request as ready for review January 6, 2026 16:18
@eszkadev
Copy link
Contributor

eszkadev commented Jan 7, 2026

If list has long entries it might happen we don't show right side of an input border:
Screenshot From 2026-01-07 08-38-08

Can be fixed later

@eszkadev eszkadev merged commit 8cb7fcc into main Jan 7, 2026
15 checks passed
@eszkadev eszkadev deleted the private/minion/push-orzzzonwluut branch January 7, 2026 07:41
@github-project-automation github-project-automation bot moved this from To Test to Done in Collabora Online Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants