Implement type ahead filtering (this time for real and without abusing the

vertically sorted pose list). When enabled typing will filter based on the
currently visible attribute columns. Using shift-space as a delimiter
independent filtering strings can be typed, so you can filter based on multiple
attributes at once to refine results while you type. Filtering stays active
until you cancel it using the escape key. While the filtered result is displayed
all normal file operations can be used. Using the return key while filtering
auto-selects and opens the first filter result, allowing for fast traversal
through directories and directly opening the topmost result.

* Introduces fFilteredPoseList which stores the active filter result. The list
  is only used when filtering is currently active, so no syncing is required
  otherwise.
* Some minor adjustments to leave out invalidations where non-visible poses are
  updated.
* Account for the now possible multiple lists throughout BPoseView.
* Add filter string output to the CountView and made that one a bit wider.
* Added all the settings-cruft for type ahead filtering (defaults to off).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35339 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2010-01-30 08:08:58 +00:00
parent 6568077f50
commit 545ebde00c
13 changed files with 678 additions and 200 deletions

View File

@ -125,6 +125,7 @@ const uint32 kShowNavigatorChanged = 'Snvc';
const uint32 kShowSelectionWhenInactiveChanged = 'Sswi';
const uint32 kTransparentSelectionChanged = 'Trse';
const uint32 kSortFolderNamesFirstChanged = 'Sfnf';
const uint32 kTypeAheadFilteringChanged = 'Tafc';
const uint32 kDesktopFilePanelRootChanged = 'Dfpr';
const uint32 kFavoriteCountChanged = 'Fvct';

View File

@ -59,7 +59,8 @@ BCountView::BCountView(BRect bounds, BPoseView* view)
fBarberPoleMap(NULL),
fLastBarberPoleOffset(5),
fStartSpinningAfter(0),
fTypeAheadString("")
fTypeAheadString(""),
fFilterString("")
{
GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE,
R_BarberPoleBitmap, &fBarberPoleMap);
@ -186,6 +187,7 @@ BCountView::CheckCount()
fLastCount = fPoseView->CountItems();
Invalidate(TextInvalRect());
}
// invalidate barber pole area if necessary
TrySpinningBarberPole();
}
@ -209,20 +211,26 @@ BCountView::Draw(BRect updateRect)
}
BString itemString;
if (!IsTypingAhead()) {
if (IsTypingAhead())
itemString << TypeAhead();
else if (IsFiltering()) {
itemString << fLastCount << " " << Filter();
} else {
if (fLastCount == 0)
itemString << "no items";
else if (fLastCount == 1)
itemString << "1 item";
else
itemString << fLastCount << " items";
} else
itemString << TypeAhead();
}
BString string(itemString);
BRect textRect(TextInvalRect());
TruncateString(&string, B_TRUNCATE_END, textRect.Width());
TruncateString(&string, IsTypingAhead() ? B_TRUNCATE_BEGINNING
: IsFiltering() ? B_TRUNCATE_MIDDLE : B_TRUNCATE_END,
textRect.Width());
if (IsTypingAhead()) {
// use a muted gray for the typeahead
@ -346,6 +354,44 @@ BCountView::IsTypingAhead() const
}
void
BCountView::AddFilter(const char *string)
{
fFilterString += string;
Invalidate();
}
void
BCountView::RemoveFilter()
{
fFilterString.Truncate(fFilterString.Length() - 1);
Invalidate();
}
void
BCountView::CancelFilter()
{
fFilterString.Truncate(0);
Invalidate();
}
const char *
BCountView::Filter() const
{
return fFilterString.String();
}
bool
BCountView::IsFiltering() const
{
return fFilterString.Length() > 0;
}
void
BCountView::SetBorderHighlighted(bool highlighted)
{

View File

@ -63,6 +63,12 @@ public:
const char *TypeAhead() const;
bool IsTypingAhead() const;
void AddFilter(const char *string);
void RemoveFilter();
void CancelFilter();
const char *Filter() const;
bool IsFiltering() const;
void SetBorderHighlighted(bool highlighted);
private:
@ -80,6 +86,7 @@ private:
float fLastBarberPoleOffset;
bigtime_t fStartSpinningAfter;
BString fTypeAheadString;
BString fFilterString;
};
} // namespace BPrivate

View File

@ -216,7 +216,7 @@ inline void
OneCheckAndUpdate(BTextWidget *widget, BPose *, BPoseView *poseView,
BColumn *column, BPoint poseLoc)
{
widget->CheckAndUpdate(poseLoc, column, poseView);
widget->CheckAndUpdate(poseLoc, column, poseView, true);
}
@ -233,7 +233,7 @@ BPose::UpdateAllWidgets(int32, BPoint poseLoc, BPoseView *poseView)
void
BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
uint32 attrType, int32, BPoint poseLoc, BPoseView *poseView)
uint32 attrType, int32, BPoint poseLoc, BPoseView *poseView, bool visible)
{
if (poseView->ViewMode() != kListMode)
poseLoc = Location(poseView);
@ -242,7 +242,7 @@ BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
if (attrName) {
// pick up new attributes and find out if icon needs updating
if (resolvedModel->AttrChanged(attrName))
if (resolvedModel->AttrChanged(attrName) && visible)
UpdateIcon(poseLoc, poseView);
// ToDo: the following code is wrong, because this sort of hashing
@ -252,7 +252,7 @@ BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
if (widget) {
BColumn *column = poseView->ColumnFor(attrHash);
if (column)
widget->CheckAndUpdate(poseLoc, column, poseView);
widget->CheckAndUpdate(poseLoc, column, poseView, visible);
} else if (attrType == 0) {
// attribute got likely removed, so let's search the
// column for the matching attribute name
@ -261,7 +261,7 @@ BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
BTextWidget *widget = fWidgetList.ItemAt(i);
BColumn *column = poseView->ColumnFor(widget->AttrHash());
if (column != NULL && !strcmp(column->AttrName(), attrName)) {
widget->CheckAndUpdate(poseLoc, column, poseView);
widget->CheckAndUpdate(poseLoc, column, poseView, visible);
break;
}
}
@ -274,7 +274,8 @@ BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
if (resolvedModel->InitCheck() != B_OK)
return;
UpdateIcon(poseLoc, poseView);
if (visible)
UpdateIcon(poseLoc, poseView);
}
// distribute stat changes
@ -286,7 +287,7 @@ BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
if (column->StatField()) {
BTextWidget *widget = WidgetFor(column->AttrHash());
if (widget)
widget->CheckAndUpdate(poseLoc, column, poseView);
widget->CheckAndUpdate(poseLoc, column, poseView, visible);
}
}
}

View File

@ -97,7 +97,8 @@ class BPose {
BRect CalcRect(const BPoseView *) const;
void UpdateAllWidgets(int32 poseIndex, BPoint poseLoc, BPoseView *);
void UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
uint32 attrType, int32 poseIndex, BPoint poseLoc, BPoseView *view);
uint32 attrType, int32 poseIndex, BPoint poseLoc,
BPoseView *view, bool visible);
bool UpdateVolumeSpaceBar(BVolume *volume);
void UpdateIcon(BPoint poseLoc, BPoseView *);

File diff suppressed because it is too large Load Diff

View File

@ -438,9 +438,14 @@ class BPoseView : public BView {
void ReadPoseInfo(Model *, PoseInfo *);
ExtendedPoseInfo *ReadExtendedPoseInfo(Model *);
void _CheckPoseSortOrder(PoseList *list, BPose *, int32 index);
// pose creation
BPose *EntryCreated(const node_ref *, const node_ref *, const char *, int32 *index = 0);
void AddPoseToList(PoseList *list, bool visibleList, bool insertionSort,
BPose *pose, BRect &viewBounds, float &listViewScrollBy,
bool forceDraw);
BPose *CreatePose(Model *, PoseInfo *, bool insertionSort = true,
int32 *index = 0, BRect *boundsPtr = 0, bool forceDraw = true);
virtual void CreatePoses(Model **models, PoseInfo *poseInfoArray, int32 count,
@ -506,7 +511,7 @@ class BPoseView : public BView {
void DrawViewCommon(const BRect &updateRect);
// pose list handling
int32 BSearchList(const BPose *, int32 *index);
int32 BSearchList(PoseList *poseList, const BPose *, int32 *index);
void InsertPoseAfter(BPose *pose, int32 *index, int32 orientation,
BRect *invalidRect);
// does a CopyBits to scroll poses making room for a new pose,
@ -607,6 +612,13 @@ class BPoseView : public BView {
virtual void AddPosesCompleted();
bool IsValidAddPosesThread(thread_id) const;
// filtering
void RemoveFilteredPose(BPose *pose, int32 index);
void FilterChanged();
bool FilterPose(BPose *pose);
void StartFiltering();
void CancelFiltering();
// misc
BList *GetDropPointList(BPoint dropPoint, BPoint startPoint, const PoseList *,
bool sourceInListMode, bool dropOnGrid) const;
@ -629,6 +641,7 @@ class BPoseView : public BView {
BRect fExtent;
// the following should probably be just member lists, not pointers
PoseList *fPoseList;
PoseList *fFilteredPoseList;
PoseList *fVSPoseList;
PoseList *fSelectionList;
NodeSet fInsertedNodes;
@ -682,6 +695,12 @@ class BPoseView : public BView {
bool fIsWatchingDateFormatChange : 1;
bool fHasPosesInClipboard : 1;
bool fCursorCheck : 1;
bool fFiltering : 1;
BObjectList<BString> fFilterStrings;
int32 fLastFilterStringCount;
int32 fLastFilterStringLength;
BRect fStartFrame;
BRect fSelectionRect;
@ -924,13 +943,13 @@ BPoseView::IndexOfColumn(const BColumn* column) const
inline int32
BPoseView::IndexOfPose(const BPose *pose) const
{
return fPoseList->IndexOf(pose);
return (fFiltering ? fFilteredPoseList : fPoseList)->IndexOf(pose);
}
inline BPose *
BPoseView::PoseAtIndex(int32 index) const
{
return fPoseList->ItemAt(index);
return (fFiltering ? fFilteredPoseList : fPoseList)->ItemAt(index);
}
inline BColumn *
@ -954,9 +973,10 @@ BPoseView::LastColumn() const
inline int32
BPoseView::CountItems() const
{
return fPoseList->CountItems();
return (fFiltering ? fFilteredPoseList : fPoseList)->CountItems();
}
inline void
BPoseView::SetMultipleSelection(bool state)
{
@ -1038,19 +1058,19 @@ BHScrollBar::SetTitleView(BView *view)
inline BPose *
BPoseView::FindPose(const Model *model, int32 *index) const
{
return fPoseList->FindPose(model, index);
return (fFiltering ? fFilteredPoseList : fPoseList)->FindPose(model, index);
}
inline BPose *
BPoseView::FindPose(const node_ref *node, int32 *index) const
{
return fPoseList->FindPose(node, index);
return (fFiltering ? fFilteredPoseList : fPoseList)->FindPose(node, index);
}
inline BPose *
BPoseView::FindPose(const entry_ref *entry, int32 *index) const
{
return fPoseList->FindPose(entry, index);
return (fFiltering ? fFilteredPoseList : fPoseList)->FindPose(entry, index);
}

View File

@ -442,6 +442,13 @@ WindowsSettingsView::WindowsSettingsView(BRect rect)
new BMessage(kSortFolderNamesFirstChanged));
fSortFolderNamesFirstCheckBox->ResizeToPreferred();
AddChild(fSortFolderNamesFirstCheckBox);
rect.OffsetBy(0, itemSpacing);
fTypeAheadFilteringCheckBox = new BCheckBox(rect, "", "Enable type-ahead filtering",
new BMessage(kTypeAheadFilteringChanged));
fTypeAheadFilteringCheckBox->ResizeToPreferred();
AddChild(fTypeAheadFilteringCheckBox);
}
@ -464,6 +471,7 @@ WindowsSettingsView::AttachedToWindow()
fShowFullPathInTitleBarCheckBox->SetTarget(this);
fOutlineSelectionCheckBox->SetTarget(this);
fSortFolderNamesFirstCheckBox->SetTarget(this);
fTypeAheadFilteringCheckBox->SetTarget(this);
}
@ -527,6 +535,15 @@ WindowsSettingsView::MessageReceived(BMessage *message)
break;
}
case kTypeAheadFilteringChanged:
{
settings.SetTypeAheadFiltering(fTypeAheadFilteringCheckBox->Value() == 1);
send_bool_notices(kTypeAheadFilteringChanged, "TypeAheadFiltering",
fTypeAheadFilteringCheckBox->Value() == 1);
Window()->PostMessage(kSettingsContentsModified);
break;
}
default:
_inherited::MessageReceived(message);
break;
@ -564,12 +581,18 @@ WindowsSettingsView::SetDefaults()
"TransparentSelection", true);
}
if (settings.SortFolderNamesFirst()) {
if (!settings.SortFolderNamesFirst()) {
settings.SetSortFolderNamesFirst(true);
send_bool_notices(kSortFolderNamesFirstChanged,
"SortFolderNamesFirst", true);
}
if (settings.TypeAheadFiltering()) {
settings.SetTypeAheadFiltering(false);
send_bool_notices(kTypeAheadFilteringChanged,
"TypeAheadFiltering", true);
}
ShowCurrentSettings();
}
@ -583,7 +606,8 @@ WindowsSettingsView::IsDefaultable() const
|| settings.SingleWindowBrowse() != false
|| settings.ShowNavigator() != false
|| settings.TransparentSelection() != true
|| settings.SortFolderNamesFirst() != true;
|| settings.SortFolderNamesFirst() != true
|| settings.TypeAheadFiltering() != false;
}
@ -623,6 +647,12 @@ WindowsSettingsView::Revert()
"SortFolderNamesFirst", fSortFolderNamesFirst);
}
if (settings.TypeAheadFiltering() != fTypeAheadFiltering) {
settings.SetTypeAheadFiltering(fTypeAheadFiltering);
send_bool_notices(kTypeAheadFilteringChanged,
"TypeAheadFiltering", fTypeAheadFiltering);
}
ShowCurrentSettings();
}
@ -639,6 +669,7 @@ WindowsSettingsView::ShowCurrentSettings()
fOutlineSelectionCheckBox->SetValue(settings.TransparentSelection()
? B_CONTROL_OFF : B_CONTROL_ON);
fSortFolderNamesFirstCheckBox->SetValue(settings.SortFolderNamesFirst());
fTypeAheadFilteringCheckBox->SetValue(settings.TypeAheadFiltering());
}
@ -652,6 +683,7 @@ WindowsSettingsView::RecordRevertSettings()
fShowNavigator = settings.ShowNavigator();
fTransparentSelection = settings.TransparentSelection();
fSortFolderNamesFirst = settings.SortFolderNamesFirst();
fTypeAheadFiltering = settings.TypeAheadFiltering();
}
@ -664,7 +696,8 @@ WindowsSettingsView::IsRevertable() const
|| fSingleWindowBrowse != settings.SingleWindowBrowse()
|| fShowNavigator != settings.ShowNavigator()
|| fTransparentSelection != settings.TransparentSelection()
|| fSortFolderNamesFirst != settings.SortFolderNamesFirst();
|| fSortFolderNamesFirst != settings.SortFolderNamesFirst()
|| fTypeAheadFiltering != settings.TypeAheadFiltering();
}

View File

@ -122,12 +122,14 @@ class WindowsSettingsView : public SettingsView {
BCheckBox *fShowSelectionWhenInactiveCheckBox;
BCheckBox *fOutlineSelectionCheckBox;
BCheckBox *fSortFolderNamesFirstCheckBox;
BCheckBox *fTypeAheadFilteringCheckBox;
bool fShowFullPathInTitleBar;
bool fSingleWindowBrowse;
bool fShowNavigator;
bool fTransparentSelection;
bool fSortFolderNamesFirst;
bool fTypeAheadFiltering;
typedef SettingsView _inherited;
};

View File

@ -449,13 +449,15 @@ BTextWidget::StopEdit(bool saveChanges, BPoint poseLoc, BPoseView *view,
void
BTextWidget::CheckAndUpdate(BPoint loc, const BColumn *column, BPoseView *view)
BTextWidget::CheckAndUpdate(BPoint loc, const BColumn *column, BPoseView *view,
bool visible)
{
BRect oldRect;
if (view->ViewMode() != kListMode)
oldRect = CalcOldRect(loc, column, view);
if (fText->CheckAttributeChanged() && fText->CheckViewChanged(view)) {
if (fText->CheckAttributeChanged() && fText->CheckViewChanged(view)
&& visible) {
BRect invalRect(ColumnRect(loc, column, view));
if (view->ViewMode() != kListMode)
invalRect = invalRect | oldRect;

View File

@ -75,7 +75,7 @@ public:
void StopEdit(bool saveChanges, BPoint loc, BPoseView *, BPose *, int32 index);
void SelectAll(BPoseView *view);
void CheckAndUpdate(BPoint, const BColumn *, BPoseView *);
void CheckAndUpdate(BPoint, const BColumn *, BPoseView *, bool visible);
uint32 AttrHash() const;
bool IsEditable() const;

View File

@ -70,6 +70,7 @@ class TTrackerState : public Settings {
BooleanValueSetting *fTransparentSelection;
BooleanValueSetting *fSortFolderNamesFirst;
BooleanValueSetting *fHideDotFiles;
BooleanValueSetting *fTypeAheadFiltering;
BooleanValueSetting *f24HrClock;
@ -174,6 +175,7 @@ TTrackerState::LoadSettingsIfNeeded()
Add(fTransparentSelection = new BooleanValueSetting("TransparentSelection", true));
Add(fSortFolderNamesFirst = new BooleanValueSetting("SortFolderNamesFirst", true));
Add(fHideDotFiles = new BooleanValueSetting("HideDotFiles", false));
Add(fTypeAheadFiltering = new BooleanValueSetting("TypeAheadFiltering", false));
Add(fSingleWindowBrowse = new BooleanValueSetting("SingleWindowBrowse", false));
Add(fShowNavigator = new BooleanValueSetting("ShowNavigator", false));
@ -387,6 +389,20 @@ TrackerSettings::SetHideDotFiles(bool hide)
}
bool
TrackerSettings::TypeAheadFiltering()
{
return gTrackerState.fTypeAheadFiltering->Value();
}
void
TrackerSettings::SetTypeAheadFiltering(bool enabled)
{
gTrackerState.fTypeAheadFiltering->SetValue(enabled);
}
bool
TrackerSettings::ShowSelectionWhenInactive()
{

View File

@ -94,6 +94,8 @@ class TrackerSettings {
void SetSortFolderNamesFirst(bool);
bool HideDotFiles();
void SetHideDotFiles(bool hide);
bool TypeAheadFiltering();
void SetTypeAheadFiltering(bool enabled);
bool ShowSelectionWhenInactive();
void SetShowSelectionWhenInactive(bool);