Getting Bounding Box of Caret in an editable WebView - cocoa

For a word processor I am writing I need to know sometimes the caret coordinates (the reason is long and uninteresting for the question itself). I writing the app. in Objective-C. I don't want to make calls to JavaScript and I don't want to make "tricks" like altering the DOM to get the caret coordinates (one solution would be to insert an empty span at the caret position and measure it bounding box). I've searched for methods that get the selected domrange bounding box with no success.
The only thing I found was a caretRectAtNode:offset:affinity in WebCoreFrameBridge (and other methods that seem to do what I want). So I see basically two answers to my question
1) There is a magic method in WebView that I am not considering
2) Tell me how to call methods inside WebCoreFrameBridge using Obj-C, and having a reference to a WebView/Frame/etc.

Related

NSTextStorageDelegate's textStorage(_,willProcessEditing:,range:,changeInLength:) moves selection

I'm trying to implement a syntax-coloring text editor that also does things like insert whitespace at the start of a new line for you, or replace text with text attachments.
After perusing the docs again after a previous implementation had issues with undoing, it seems like the recommended bottleneck for this is NSTextStorageDelegate's textStorage(_,willProcessEditing:,range:,changeInLength:) method (which states that Delegates can change the characters or attributes., whereas didProcessEditing says I can only change attributes). This works fine, except that whenever I actually change attributes or text, the text insertion mark moves to the end of whatever range of text I modify (so if I change the style of the entire line, the cursor goes at the end of the line).
Does anybody know what additional call(s) I am missing that tell NSTextStorage/NSTextView not to screw up the insertion mark? Also, once I insert text, I might have to tell it to move the insertion mark to account for text I've inserted.
Note: I've seen Modifying NSTextStorage causes insertion point to move to the end of the line, but that assumes I'm subclassing NSTextStorage, so I can't use the solution there (and would rather not subclass NSTextStorage, as it's a semi-abstract subclass and I'd lose certain behaviours of Apple's class if I subclassed it).
I found out the source of the problem.
And the only solution that will work robustly based on reasons inherent to the Cocoa framework instead of mere work-arounds. (Note there's probably at least one other, metastable approach based on a ton of quick-fixes that produces a similar result, but as metastable alternatives go, that'll be very fragile and require a ton of effort to maintain.)
TL;DR Problem: NSTextStorage collects edited calls and combines the ranges, starting with the user-edited change (e.g. the insertion), then adding all ranges from addAttributes(_:range:) calls during highlighting.
TL;DR Solution: Perform highlighting from textDidChange(_:) exclusively.
Details
This only applies to a single processEditing() run, both in NSTextStorage subclasses and in NSTextStorageDelegate callbacks.
The only safe way to perform highlighting I found is to hook into NSText.didChangeNotification or implement NSTextDelegate.textDidChange(_:).
As per #Willeke's comments to the OP's question, this is the best place to perform changes after the layout pass. But as opposed to the comment thread, setting back NSText.selectedRange does not suffice. You won't notice the problem of post-fixing the selection after the caret has moved away until
you highlight whole blocks of text,
spanning multiple lines, and
exceeding the visible (NSClipView) boundaries of the scroll view.
In this rare case, most keystrokes will make the scroll view jiggle or bounce around. But there's no additional quick-fix against this. I tried. Neither preventing sending the scroll commands from private API in NSLayoutManager nor avoiding scrolling by overriding all methods with "scroll" in them from a NSTextView subclass works well. You can stop scrolling to the insertion point altogether, sure, but no such luck getting a solid algorithm out that does not scroll only when you perform highlighting.
The didChangeNotification approach does work reliably in all situations I and my app's testers were able to come up with (including a crash situation as weird as scrolling the text and then, during the animation, replacing the string with something shorter -- yeah, try to figure that kind of stuff out from crash logs that report invalid glyph generation ...).
This approach works because it does 2 glyph generation passes:
One pass for the edited range, in the case of typing for every key stroke with a NSRange of length 1, sending the edited notification with both [.editedCharacters, .editedAttributes], the former being responsible for moving the caret;
another pass for whatever range is affected by syntax highlighting, sending the edited notification with [.editedAttributes] only, thus not affecting the caret's position at all.
Even more details
In case you want to know more about the source of the problem, I put more my research, different approaches, and details of the solution in a much longer blog post for reference. This here, though, is the solution itself. http://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/
The above accepted answer with the notification center worked for me, but I had to include one more thing when editing text. (Which may be different from selection).
The editedRange of the NSTextStorage was whack after the notification center callback. So I keep track of the last known value myself by overriding the processEditing function and using that value later when I get the callback.
override func processEditing() {
// Hack.. the editedRange property when reading from the notification center callback is weird
lastEditedRange = editedRange
super.processEditing()
}

How do I make two NSWindow appear to have the same order?

I have two NSWindows, one of which is the child of the other. Basically what I want to accomplish is for the child to pop out and appear to extend the parent. If you like, think of it as opening a book: the cover flips out and ends up at the same Z order as the title page.
However I have the problem that one of the windows is always casting a shadow on the other. Is there any way to have them be on the same Z, so that neither casts a shadow on the other, but both cast shadows on windows below them?
Could this work? Take your existing child window, and remove the shadow with a setShadow:false call. Meanwhile create a third dummy window Z-ordered behind the two existing windows. Make it so that this window is always the same size and shape as the true child window and leave the shadow on.
Two possible problems with this: (1) a weird 'double shadow' at the point(s) where the two shadows intersect. (2) The shadow that the window with the focus casts is much larger than the shadow that windows without the focus cast. So if focus shifts between your two windows, the shadow might change size. Not sure how you'd get around that problem.
Otherwise your only choice may be to put both your windows with shadows turned off in a yucky container window which casts the one shadow. This would be a pretty radical UI change, but in my experience apps that tinker with the default appearance of their application's 'outer edge' or try to code their own partial transparency without using OS features rarely run smoothly.

How to break a sentence in Xcode

I'm new to this, and new to coding.
I'm working on a quiz app, and here is one of the lines of code:
[questions addObject:#"When was the Declaration of Independence signed?"];
[answer addObject:#"1776"];
But when I test it, the question is too long, so it makes the text extra small, how can I make it so half of it will drop down to the next line?
There are a couple options.
The most basic is to "hard-code" the line break, which is NOT the best option, but I'll spell it out so that you are aware of the difference: this involves splitting the question into two lines by adding a "newline" code (\n) at the point in the question where you want the newline to start. For example:
[questions addObject:#"When was the Declaration\nof Independence signed?"];
This isn't flexible or adaptive, and whether you are using the Interface Builder to configure the object displaying the text (UILabel or UITextView, or other class), the second line of the text may disappear because it falls below the visible area set-up when you create the UILabel or UITextView. This method also goes against best practices because it confounds display with data. There is nothing wrong with your data as it stands.
The better option is to work ONLY with the object displaying the text - which you'd need to do anyway. I'll use UILabel as an example here, but both UILabel and UITextView objects have properties you can set programmatically or in Interface Builder that will effectively enable them not only to stretch their display area instead of shrinking your text, but also to wrap your text at the most logical point based on the new size of the display area.
Programmatically, first make sure that the number of lines for your UILabel object is set to 0:
textLabel.numberOfLines = 0;
You can also do this in Interface Builder if that's where you've created your UILabel.
If that doesn't work, something else is probably not set-up properly - check on the UILabel's metrics to make sure you haven't "locked" its size in any way (its ability to auto-grow or auto-shrink based on the size of its contents).

Get the position of an object on a Matlab plot

How is it possible to get, directly from the Matlab command window, the position (i.e. the coordinates) of an object (e.g. an arrow, a rectangle or sim.) that I have drawn on a plot?
You can usually do this using the handle graphics properties. For example:
Make a plot
h = plot(1:10, rand(10,1));
Then get the actual values of the points
x = get(h,'xdata')
y = get(h,'ydata')
Different types of objects have different properties, sometimes you have to explore. In that case this syntax is useful.
get(h) %This displays all available properties on `h` to the command window
A final useful tidbit is the gco ("get current object") function, which provides the handle of the last item that you plotted or manually clicked on. This can help if you're not sure where the plotted item came from.
Edit:
To find all of the properties which are descendents of an object, use either findobj, or findall. For example:
findobj(gcf); %Returns all non-hidden, typical objects. This should be your first attempt.
findall(gcf); %Returns all children, even hidden object, such as titles, manually added annotations, and UI menus
This call removes some common UI annotations
get(findall(gcf,'-not','type','uimenu','-not','type','uitoggletool','-not','type','uipushtool','-not','type','uitogglesplittool'),'type')
(Presumably the last example could be improved with a properly designed regexp, but I can't seem to get that working right now.)

Stacking widgets in Gtk+

Is there a way in Gtk+ to stack one widget on top of another -- not counting GtkFixed? GtkFixed doesn't work well for two reasons: 1) I need Z order, and 2) I need one widget to stretch and fill provided space.
I had this exact issue using a Gtk::Fixed (actually gtk.Fixed -- pygtk -- but I think it's all the same underneath) and I was able to handle it quite easily by manipulating each widget's window.
In my case, the widgets already are EventBox instances, and I just needed to make sure that the one I was dragging around was on top, because otherwise it slid underneath others, which looked quite wrong. The solution was as simple as calling "widget.window.raise_()" to raise the widget's underlying window when the widget was clicked to begin the drag.
So I'm basically just reaffirming that the previous answer works, but I wanted to point out that it's actually pretty easy. It sounds like you may need to create some EventBoxes to hold your widgets, but after that it should just work.
You can see the code I was working on at http://github.com/divegeek/BlockHead
I don't think there is a proper container in standard GTK. I would subclass Gtk::Fixed... it is still the closest one you can get, and if you use gtkmm then subclassing shouldn't be very difficult¹. Then you can control the dimensions of all widgets, stretching one selected child to fill space.
To control Z-axis you will probably need to manipulate widget's X windows--check GDK documentation on topic of GDK windows². I remember that in PyGTK each widget has gtk.Widget.window property, I guess that the same is for gtkmm. This assumes that all your child widgets have X windows, so f.e. you'll need to wrap Gtk::Label inside Gtk::EventBox.
¹ http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/chapter-customwidgets.html
² http://www.gtkmm.org/docs/gtkmm-2.4/docs/reference/html/classGdk_1_1Window.html#6eef65b862344ad01b01e527f2c39741
I was able to do exactly that -
display one widget filling the entire space, and then another on top at a fixed location, both visible at the same time
in gtk2 by using GtkFixed, working out a suitable insertion order (first inserted - bottom, last inserted - top), and - most importantly!! - forcing GtkFixed to have it's own window:
`f := gtk_fixed_new();
gtk_widget_set_has_window(f, 1);`
If you don't do set_has_window(f, 1), all children windows will be inserted into some parent widget/window (see gtk_fixed_realize() in gtkfixed.c), and that might create z-order mess.

Resources