-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Text Editor: Support custom styles #494
Conversation
Ready for review! A few notes:
|
This uppercase only version was made by manually replacing all lowercase glyphs with their uppercase versions. This means that the font is always displayed as uppercase, but the actual casing of the text entered is preserved and will be remembered when switching to a non-uppercase font.
It's not possible to identify a typeface by inspecting a TextView. To be able to pass this information between the text editor and the canvas, we'll use a custom TextView that keeps a reference to a unique typeface ID that can be resolved to an actual font.
This allows PhotoEditor to convert TypefaceIds to concrete typefaces without knowing the details of the stories module's supported custom fonts.
Level with image buttons around it, shrink text size a bit.
347bff8
to
ea20583
Compare
This is so cool @aforcier!
This is interesting. Also, we've learned from past experiences in Aztec that using InputFilter is problematic wordpress-mobile/AztecEditor-Android#580 when users have a soft keyboard other than the device's setup (i.e. SwitfKey or other apps), so I think your approach is probably the best path forward too.
I think it's fine - we should keep Cyrillic if it's already supported. I don't know much about fonts but I wonder if there exists a upper case / lower case mode for Cyrillic we may be concerned about? For example in German nouns always start with upper case, and it's considered a misspelling not to do so. Wonder if such a thing like "having all caps is a misspelling" exists in any language 🤔 - probably a nitpick.
I think it's good - as we were discussed elsewhere, we'll need an univocal way to identify this, which also comes as a strong need between devices / platforms, so I think it's good this came up earlier.
These are neat! Gave it a spin, these are my notes. Fallback mechanism if font not presentI wondered what if downloading the fonts go wrong? Can we still use the app? What's the fallback mechanism? I nice thing to try might be to install the app on a new (preferably older) emulator with airplane mode ON. Effectively tried installing this on a Pixel 3XL API 29 emulator with airplane mode ON, the app launched (logs show it failed to retrieve the fonts) and then when I tried to add text it crashed, here we have the logs:
Also curious note: just turning airplane mode back OFF didn't automatically trigger the font downloading process (not that I found anything that says so, but that was my expectation somehow). Waited a few minutes and nothing interesting appeared in the logs. However, it was fetched when I first wanted to access the feature (when I tapped the text control on the slide composer), when hitting
I wonder if this could have any impact on slow connections (given these are downloaded and shown right there. We should probably do something to wait for fonts to be fully loaded, or have a fallback mechanism if font is not present. The fallback mechanism should include reflecting the availability of fonts on the selector, ideally? (for example, by not showing all of them, or only showing the default font, or just not showing the selector at all). EmojiCompatI wondered about things like how does this play with I verified this will still work because of how EmojiCompat works (https://developer.android.com/guide/topics/ui/look-and-feel/emoji-compat) -> the library identifies emoji for a given CharSequence, replaces them with EmojiSpans, and then renders the glyphs from the passed font (using Other thoughts / notes
Will now go through code and leave notes there if anything 👍 |
* This allows typeface data to be serialized, since there's otherwise | ||
* no way to identify which typeface is active on a TextView. | ||
*/ | ||
data class IdentifiableTypeface(@TypefaceId val id: Int, val typeface: Typeface?) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL https://kotlinlang.org/docs/reference/annotations.html and everything related.
Ah yes - I felt uneasy as soon as I even typed
From my own basic knowledge and from asking around/researching, I don't think there's an issue there. I mapped all lowercase Cyrillic characters to uppercase in the font and it's pretty common for all-caps to be used, at least in Russian. If things are different in another Cyrillic-using language, that's a pretty out there edge case we can deal with if anyone ever brings up an issue with it I'd say.
Ouf, nice catch there. I have an idea for how to approach this I'll try out and update on.
Awesome, thanks for verifying that - I did check that emoji were working but I didn't dive into the |
Right - I remember I was a bit thrown here when I started working on the feature because I realized the EmojiCompat declaration and download logic was in the
I don't think this is a huge deal but I see how it might feel like a bug. cc @megsfulton do you think we should change the behavior? |
Updated the TODO list with the fallback issue and some fresh design feedback from Megs, working through the list. |
235b5f0
to
8d871c1
Compare
@mzorz this is ready for a second pass 🎉 Added a fallback method for fonts in case we're not able to download the custom ones (21b2b65)Here we'll basically fall back to the default font and request the closest style we can to the original (i.e. Serif, Sans Serif, Monospace, Default Bold). The rest of the styles (spacing, shadow, size) are still applied. If the fonts are downloaded later, editing an existing text in fallback style will apply the proper font. I ran this fallback behavior by Megs and it sounded good to her 👍 Now hiding the text from the background while editing (8d871c1)Megs pointed out that it's pretty distracting to write over a second copy of the text when editing text that was already added to the view. I see we were already hiding the TextView when it was empty, I modified things to always hide the currently editing view. Issue with clipping (not to be resolved in this PR)This one's a bit trickier than I initially thought. I have an approach in mind but it's going to affect text background color too. I opened #508 and I'll address the issue probably next sprint when I have an approach working for background color I can test against. Assorted style tweaks (not applied in this PR yet)I sent @megsfulton a test APK with some of the style changes that are still unchecked in the list - I'll update the PR when we've settled those values. Hold off merging until then please @mzorz. |
Cool! This would close this issue here then #434 👍
Awesome! Nice you found a way to make it more robust 👍 😄
Sounds good - doesn't sound like a blocker; wonder how bad the problem is? Let me know if I can help 🙇
Sure thing! Nice work on this second pass as well @aforcier thank you! |
Okay @mzorz , ready for round 3! 🎉 Changes:
Megs pointed out what was basically a regression on #180 - the line breaks in the added text view didn't always match what was shown in the editor. The cause was that I added some padding to the editor text in #466 that needed to be reflected in the added text view. This was done in a2ac933, but I wanted to mention that it also fixes part of the text clipping issue (#508). You can check this by using the 'Playful' font before and after the patch, and starting new lines with lowercase This still leaves some more text clipping issues, I think they're mostly just related to the Bold style. I have a fix in mind which I'll get to with the background color work as I mentioned. |
As said before this is delightful!
Let me know what you think and feel free to merge 👍 Thanks a lot this is beautiful! 😄 🎉 |
Oh interesting one - I don't have any quick ideas, we're not doing anything super special. I suppose we might need to re-draw the view (and probably re-apply the selection)? That solution seems like it might be janky though and worse than the actual problem. 🤔 I don't think it's a huge deal (the problem is visual only, the actual selection is correctly preserved as far as I can tell), so I opened #510 and we can revisit it later.
🙇 props once more to @megsfulton for designing this thing of beauty 😀 |
Closes #185. Adds a button to the text editor which cycles through six text styles. The text styles each have a particular typeface, along with rules like text size, line spacing, letter spacing, and shadow.
Remaining TODOs
General
Look into fallback fonts for other scripts, probably create a new issue-> Text Editing: Support fallback fonts #498, basicallyNot-MVP
PR feedback
Design feedback
Adjust scrim overlay further pending feedbackAdjust line height for Oswald, maybe othersSupport smart quotes/apostrophes-> Text Editing: Support smart quotes #503,Not-MVP
Fix clipping of text on the left for certain fonts and letters-> Text Editing: Fix clipping of text on the left for certain fonts and letters #508, will fix in its own PRImplementation notes
Font loading
I went with Android downloadable fonts, which allows us to save on apk size considerably by letting Android asynchronously download the fonts (or not if they already exist on the device because another app downloaded them already) instead of bundling them in our apk. There is one exception, Oswald, which I added directly as a
ttf
file:Oswald
The designs called for Oswald to be uppercase. There isn't a variation of the font like this, so I looked into a few approaches. A simple solution is to change the input filter to all caps, and turn the text uppercase. We wouldn't want to permanently make all the text uppercase though, since someone just cycling through fonts would have to re-write their text. I started to back up the text and restore it after cycling through Oswald, which works but it's still not clear what should happen when changes are made to the text while Oswald is selected.
I looked at some other apps - some seem to do what I did, but some were clearly not modifying the input filter because the keyboard wasn't forced to all caps. One possible way they might have done this would be to have a custom
EditText
implementation that has separate 'inputted text' and 'displayed text'. This isn't trivial though, and we'd need to keep track of this when handing the text off to be added as aTextView
in the canvas, and back to being edited.Instead, I opened Oswald in a font glyph editor and created a modified version where all the lowercase glyphs were replaced with their uppercase versions.
So for example, the glyph for
U+0061
, which is lowercasea
, is mapped to a glyph that looks likeA
.This way we can avoid any custom uppercase logic in the code, and all text entered is respected without us having to do anything, because the underlying code points are maintained.
The cost is that we have to include the font file in our apk, which is ~100KB. Since it's the only one that feels okay for now, we could even reduce the size by removing Cyrillic support which is included in Oswald, but is pretty much the only non-Latin Extended script any of our custom fonts support.
IdentifiableTypeface
Stories does some text serializing/deserializing since anything we created in the text editor is replicated in a TextView, and back. This isn't a problem for most text styling (size, letter spacing, color, etc), but is a problem for typefaces. The reason is that once you set a view's
Typeface
, there's no way I could fine to reverse lookup that back into a font ID, so we can't tell what rules go with that typeface, and we can't even serialize it.I solved this by using a custom TextView which deprecates the usual typeface methods and instead supports a custom
IdentifiableTypeface
class, which is just a wrapper forTypeface
with a unique ID.TextStyler
andFontResolver
We're starting to pass a lot of style parameters back and forth between
PhotoEditor
andComposeLoopFrameActivity
. I decided to encapsulate this in aTextStyler
class that can be passed around more easily. SincePhotoEditor
probably shouldn't know what custom fonts we support, I'm also using aFontResolver
interface to let the stories module tellPhotoEditor
which typeface is associated with which ID.To test
But also,