Skip to content
This repository has been archived by the owner on Sep 4, 2024. It is now read-only.

Commit

Permalink
[Mac] Add TextEntry completion support
Browse files Browse the repository at this point in the history
  • Loading branch information
sevoku committed Dec 18, 2019
1 parent a4018c7 commit bbf268b
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 39 deletions.
62 changes: 34 additions & 28 deletions Xwt.XamMac/Xwt.Mac/ComboBoxEntryBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public ComboBoxEntryBackend ()
public override void Initialize ()
{
base.Initialize ();
ViewObject = new MacComboBox (EventSink, ApplicationContext);
ViewObject = new MacComboBox (this);
}

protected override Size GetNaturalSize ()
Expand All @@ -56,7 +56,11 @@ protected override Size GetNaturalSize ()
public ITextEntryBackend TextEntryBackend {
get {
if (entryBackend == null)
{
entryBackend = new Xwt.Mac.TextEntryBackend ((MacComboBox)ViewObject);
if (Widget.Delegate is TextFieldDelegate)
((TextFieldDelegate)Widget.Delegate).Backend = entryBackend;
}
return entryBackend;
}
}
Expand Down Expand Up @@ -102,7 +106,31 @@ public void SetTextColumn (int column)
#endregion
}

class MacComboBox : NSComboBox, IViewObject, INSComboBoxDelegate
class MacComboBoxDelegate : TextFieldDelegate, INSComboBoxDelegate
{
WeakReference weakBackend;

public ComboBoxEntryBackend ComboBoxEntryBackend
{
get { return weakBackend?.Target as ComboBoxEntryBackend; }
set { weakBackend = new WeakReference(value); }
}

[Export("comboBoxSelectionDidChange:")]
public void SelectionChanged (NSNotification notification)
{
var combobackend = ComboBoxEntryBackend;
if (combobackend?.EventSink != null)
{
combobackend.ApplicationContext.InvokeUserCode(delegate {
Backend?.EventSink?.OnChanged();
combobackend.EventSink.OnSelectionChanged();
});
}
}
}

class MacComboBox : NSComboBox, IViewObject
{
IComboBoxEventSink eventSink;
ITextEntryEventSink entryEventSink;
Expand All @@ -111,11 +139,11 @@ class MacComboBox : NSComboBox, IViewObject, INSComboBoxDelegate
int cacheSelectionStart, cacheSelectionLength;
bool checkMouseMovement;

public MacComboBox (IComboBoxEventSink eventSink, ApplicationContext context)
public MacComboBox (ComboBoxEntryBackend backend)
{
this.context = context;
this.eventSink = eventSink;
Delegate = this;
context = backend.ApplicationContext;
eventSink = backend.EventSink;
Delegate = new MacComboBoxDelegate { ComboBoxEntryBackend = backend };
}

public void SetEntryEventSink (ITextEntryEventSink entryEventSink)
Expand All @@ -131,34 +159,12 @@ public NSView View {

public ViewBackend Backend { get; set; }

[Export ("comboBoxSelectionDidChange:")]
public new void SelectionChanged (NSNotification notification)
{
if (entryEventSink != null) {
context.InvokeUserCode (delegate {
entryEventSink.OnChanged ();
eventSink.OnSelectionChanged ();
});
}
}

public override void DidChange (NSNotification notification)
{
base.DidChange (notification);
if (entryEventSink != null) {
context.InvokeUserCode (delegate {
entryEventSink.OnChanged ();
entryEventSink.OnSelectionChanged ();
});
}
}
public override void KeyUp (NSEvent theEvent)
{
base.KeyUp (theEvent);
HandleSelectionChanged ();
}


NSTrackingArea trackingArea;
public override void UpdateTrackingAreas ()
{
Expand Down
108 changes: 97 additions & 11 deletions Xwt.XamMac/Xwt.Mac/TextEntryBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
// THE SOFTWARE.

using System;
using System.Linq;
using AppKit;
using CoreGraphics;
using Foundation;
Expand Down Expand Up @@ -52,7 +53,7 @@ public override void Initialize ()
if (ViewObject is MacComboBox) {
((MacComboBox)ViewObject).SetEntryEventSink (EventSink);
} else if (ViewObject == null) {
var view = new CustomTextField (EventSink, ApplicationContext);
var view = new CustomTextField (EventSink, ApplicationContext) { Backend = this };
ViewObject = new CustomAlignedContainer (EventSink, ApplicationContext, (NSView)view) { DrawsBackground = false };
Container.ExpandVertically = true;
MultiLine = false;
Expand Down Expand Up @@ -232,16 +233,51 @@ void HandleSelectionChanged ()
}
}

string[] completions;
Func<string, string, bool> completionsMatchFunc;

public bool HasCompletions {
get { return false; }
get {
return completions?.Length > 0;
}
}

public void SetCompletions (string[] completions)
{
this.completions = completions;
if (completions != null) {
var entryDelegate = Widget.Delegate;
if (entryDelegate == null) {
entryDelegate = Widget.Delegate = new TextFieldDelegate ();
}
if (entryDelegate is TextFieldDelegate) {
((TextFieldDelegate)entryDelegate).Backend = this;
}
}
if (completionsMatchFunc == null) {
completionsMatchFunc = DefaultCompletionMatchFunc;
}
}

public void SetCompletionMatchFunc (Func<string, string, bool> matchFunc)
{
completionsMatchFunc = matchFunc ?? DefaultCompletionMatchFunc;
}

bool DefaultCompletionMatchFunc (string word, string completion)
{
if (word == null || completion == null)
return false;
return completion.StartsWith (word, StringComparison.CurrentCulture);
}

internal string [] GetCompletions (string word)
{
if (completions?.Length > 0)
{
return completions.Where (c => completionsMatchFunc(word, c)).ToArray ();
}
return new string[0];
}

#endregion
Expand Down Expand Up @@ -283,6 +319,65 @@ public override Drawing.Color BackgroundColor {
Widget.Cell.BackgroundColor = value.ToNSColor ();
}
}

protected override void Dispose(bool disposing)
{
completions = null;
base.Dispose (disposing);
}
}

class TextFieldDelegate : NSTextFieldDelegate
{
WeakReference weakBackend;

public TextEntryBackend Backend
{
get { return weakBackend?.Target as TextEntryBackend; }
set { weakBackend = new WeakReference (value); }
}

public override string[] GetCompletions (NSControl control, NSTextView textView, string[] words, NSRange charRange, ref nint index)
{
var backend = Backend;
if (backend != null)
{
string word;
try {
word = textView.String.Substring ((int)charRange.Location, (int)charRange.Length);
} catch (ArgumentOutOfRangeException) {
return new string[0];
}
return backend.GetCompletions (word);
}
return new string[0];
}

bool isCompleting;
[Export("controlTextDidChange:")]
public void DidChange (NSNotification notification)
{
var editor = notification.Object as NSTextView ?? (notification.Object as NSTextField)?.CurrentEditor as NSTextView;
if (!isCompleting && editor != null && editor.String.Length > 0 && Backend?.HasCompletions == true) {
// Cocoa will call DidChange for each completion, even if the text didn't change
// avoid an infinite loop with an isCompleting check.
isCompleting = true;
editor.Complete (null);
isCompleting = false;
}
if (Backend.EventSink != null) {
Backend.ApplicationContext.InvokeUserCode (delegate {
Backend.EventSink.OnChanged ();
Backend.EventSink.OnSelectionChanged ();
});
}
}

protected override void Dispose(bool disposing)
{
weakBackend = null;
base.Dispose(disposing);
}
}

class CustomTextField: NSTextField, IViewObject
Expand Down Expand Up @@ -313,15 +408,6 @@ public NSView View {
}

public ViewBackend Backend { get; set; }

public override void DidChange (NSNotification notification)
{
base.DidChange (notification);
context.InvokeUserCode (delegate {
eventSink.OnChanged ();
eventSink.OnSelectionChanged ();
});
}

public override string StringValue
{
Expand Down

0 comments on commit bbf268b

Please sign in to comment.