summaryrefslogtreecommitdiffstats
path: root/browser/components/touchbar/docs/index.rst
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/touchbar/docs/index.rst')
-rw-r--r--browser/components/touchbar/docs/index.rst361
1 files changed, 361 insertions, 0 deletions
diff --git a/browser/components/touchbar/docs/index.rst b/browser/components/touchbar/docs/index.rst
new file mode 100644
index 0000000000..902eb2c849
--- /dev/null
+++ b/browser/components/touchbar/docs/index.rst
@@ -0,0 +1,361 @@
+Touch Bar
+=========
+
+The Touch Bar is a hardware component on some MacBook Pros released from 2016.
+It is a display above the keyboard that allows more flexible types of
+input than is otherwise possible with a normal keyboard. Apple offers Touch Bar
+APIs so developers can extend the Touch Bar to display inputs specific to their
+application. Firefox consumes these APIs to offer a customizable row of inputs
+in the Touch Bar.
+
+In Apple's documentation, the term "the Touch Bar" refers to the hardware.
+The term "a Touch Bar" refers not to the hardware but to a collection of inputs
+shown on the Touch Bar. This means that there can be multiple "Touch Bars" that
+switch out as the user switches contexts. The same naming convention is used in
+this document.
+
+In this document and in the code, the word "input" is used to refer to
+an interactive element in the Touch Bar. It is often interchangeable with
+"button", but "input" can also refer to any element displayed in the Touch Bar.
+
+The Touch Bar should never offer functionality unavailable to Firefox users
+without the Touch Bar. Most macOS Firefox users do not have the Touch Bar and
+some choose to disable it. Apple's own `Human Interface Guidelines`_ (HIG)
+forbids this kind of Touch Bar functionality. Please read the HIG for more
+design considerations before you plan on implementing a new Touch Bar feature.
+
+If you have questions about the Touch Bar that are not answered in this
+document, feel free to reach out to `Harry Twyford`_ (:harry on Slack).
+He wrote this document and Firefox's initial Touch Bar implementation.
+
+.. _Human Interface Guidelines: https://developer.apple.com/design/human-interface-guidelines/macos/touch-bar/touch-bar-overview/
+
+.. _Harry Twyford: mailto:harry@mozilla.com
+
+.. contents:: Table of Contents
+
+Overview
+~~~~~~~~
+
+Firefox's Touch Bar implementation is equal parts JavaScript and Cocoa
+(Objective-C++). The JavaScript code lives in ``browser/components/touchbar``
+and the Cocoa code lives in ``widget/cocoa``, mostly in ``nsTouchBar.mm``. The
+Cocoa code is a consumer of Apple's Touch Bar APIs and defines what types of
+Touch Bar inputs are available to its own consumers. The JS code in
+``browser/components/touchbar`` provides services to ``nsTouchBar.mm`` and
+defines what inputs the user actually sees in the Touch Bar. There is two-way
+communication between the JS and the Cocoa: the Cocoa code asks the JS what
+inputs it should display, and the JS asks the Cocoa code to update those inputs
+when needed.
+
+JavaScript API
+~~~~~~~~~~~~~~
+
+``browser/components/touchbar/MacTouchBar.sys.mjs`` defines what specific inputs are
+available to the user, what icon they will have, what action they will perform,
+and so on. Inputs are defined in the ``gBuiltInInputs`` object `in that file`_.
+When creating a new object in ``gBuiltInInputs``, the available properties are
+documented in the JSDoc for ``TouchBarInput``:
+
+.. code:: JavaScript
+
+ /**
+ * A representation of a Touch Bar input.
+ * @param {string} input.title
+ * The lookup key for the button's localized text title.
+ * @param {string} input.image
+ * A URL pointing to an SVG internal to Firefox.
+ * @param {string} input.type
+ * The type of Touch Bar input represented by the object.
+ * Must be a value from kInputTypes.
+ * @param {Function} input.callback
+ * A callback invoked when a touchbar item is touched.
+ * @param {string} [input.color]
+ * A string in hex format specifying the button's background color.
+ * If omitted, the default background color is used.
+ * @param {bool} [input.disabled]
+ * If `true`, the Touch Bar input is greyed out and inoperable.
+ * @param {Array} [input.children]
+ * An array of input objects that will be displayed as children of
+ * this input. Available only for types KInputTypes.POPOVER and
+ * kInputTypes.SCROLLVIEW.
+ */
+
+Clarification on some of these properties is warranted.
+
+* ``title`` is the key to a Fluent translation defined in ``browser/locales/<LOCALE>/browser/touchbar/touchbar.ftl``.
+* ``type`` must be a value from the ``kInputTypes`` enum in ``MacTouchBar.sys.mjs``.
+ For example, ``kInputTypes.BUTTON``. More information on input types follows
+ below.
+* ``callback`` points to a JavaScript function. Any chrome-level JavaScript can
+ be executed. ``execCommand`` is a convenience method in ``MacTouchBar.sys.mjs``
+ that takes a XUL command as a string and executes that command. For instance,
+ one input sets ``callback`` to ``execCommand("Browser:Back")``.
+* ``children`` is an array of objects with the same properties as members of
+ ``gBuiltInInputs``. When used with an input of type
+ ``kInputTypes.SCROLLVIEW``, ``children`` can only contain inputs of type
+ ``kInputTypes.BUTTON``. When used with an input of type
+ ``kInputTypes.POPOVER``, any input type except another ``kInputTypes.POPOVER``
+ can be used.
+
+.. _in that file: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/browser/components/touchbar/MacTouchBar.jsm#82
+
+Input types
+-----------
+
+Button
+ A simple button. If ``image`` is not specified, the buttons displays the text
+ label from ``title``. If both ``image`` and ``title`` are specified, only the
+ ``image`` is shown. The action specified in ``callback`` is executed when the
+ button is pressed.
+
+ .. caution::
+
+ Even if the ``title`` will not be shown in the Touch Bar, you must still
+ define a ``title`` property.
+
+Main Button
+ Similar to a button, but displayed at double the width. A main button
+ displays both the string in ``title`` and the icon in ``image``. Only one
+ main button should be shown in the Touch Bar at any time, although this is
+ not enforced.
+
+Label
+ A non-interactive text label. This input takes only the attributes ``title``
+ and ``type``.
+
+Popover
+ Initially represented in the Touch Bar as a button, a popover will display an
+ entirely different set of inputs when pressed. These different inputs should
+ be defined in the ``children`` property of the parent. Popovers can also be
+ shown and hidden programmatically, by calling
+
+ .. code:: JavaScript
+
+ gTouchBarUpdater.showPopover(
+ TouchBarHelper.baseWindow,
+ [POPOVER],
+ {true | false}
+ );
+
+ where the second argument is a reference to a popover TouchBarInput and
+ the third argument is whether the popover should be shown or hidden.
+
+Scroll View
+ A Scroll View is a scrolling list of buttons. The buttons should be defined
+ in the Scroll View's ``children`` array.
+
+ .. note::
+
+ In Firefox, a list of search shortcuts appears in the Touch Bar when the
+ address bar is focused. This is an example of a ScrollView contained within
+ a popover. The popover is opened programmatically with
+ ``gTouchBarUpdater.showPopover`` when the address bar is focused and it is
+ hidden when the address bar is blurred.
+
+Examples
+--------
+Some examples of ``gBuiltInInputs`` objects follow.
+
+A simple button
+
+ .. code:: JavaScript
+
+ Back: {
+ title: "back",
+ image: "chrome://browser/skin/back.svg",
+ type: kInputTypes.BUTTON,
+ callback: () => execCommand("Browser:Back", "Back"),
+ },
+
+ A button is defined with a title, icon, type, and a callback. The callback
+ simply calls the XUL command to go back.
+
+The search popover
+ This is the input that occupies the Touch Bar when the address bar is focused.
+
+ .. code:: JavaScript
+
+ SearchPopover: {
+ title: "search-popover",
+ image: "chrome://global/skin/icons/search-glass.svg",
+ type: kInputTypes.POPOVER,
+ children: {
+ SearchScrollViewLabel: {
+ title: "search-search-in",
+ type: kInputTypes.LABEL,
+ },
+ SearchScrollView: {
+ key: "search-scrollview",
+ type: kInputTypes.SCROLLVIEW,
+ children: {
+ Bookmarks: {
+ title: "search-bookmarks",
+ type: kInputTypes.BUTTON,
+ callback: () =>
+ gTouchBarHelper.insertRestrictionInUrlbar(
+ UrlbarTokenizer.RESTRICT.BOOKMARK
+ ),
+ },
+ History: {
+ title: "search-history",
+ type: kInputTypes.BUTTON,
+ callback: () =>
+ gTouchBarHelper.insertRestrictionInUrlbar(
+ UrlbarTokenizer.RESTRICT.HISTORY
+ ),
+ },
+ OpenTabs: {
+ title: "search-opentabs",
+ type: kInputTypes.BUTTON,
+ callback: () =>
+ gTouchBarHelper.insertRestrictionInUrlbar(
+ UrlbarTokenizer.RESTRICT.OPENPAGE
+ ),
+ },
+ Tags: {
+ title: "search-tags",
+ type: kInputTypes.BUTTON,
+ callback: () =>
+ gTouchBarHelper.insertRestrictionInUrlbar(
+ UrlbarTokenizer.RESTRICT.TAG
+ ),
+ },
+ Titles: {
+ title: "search-titles",
+ type: kInputTypes.BUTTON,
+ callback: () =>
+ gTouchBarHelper.insertRestrictionInUrlbar(
+ UrlbarTokenizer.RESTRICT.TITLE
+ ),
+ },
+ },
+ },
+ },
+ },
+
+ At the top level, a Popover is defined. This allows a collection of children
+ to be shown in a separate Touch Bar. The Popover has two children: a Label,
+ and a Scroll View. The Scroll View displays five similar buttons that call a
+ helper method to insert search shortcut symbols into the address bar.
+
+Adding a new input
+------------------
+Adding a new input is easy: just add a new object to ``gBuiltInInputs``. This
+will make the input available in the Touch Bar customization window (accessible
+from the Firefox menu bar item).
+
+If you want to to add your new input to the default set, add its identifier
+here_, where ``type`` is a value from ``kAllowedInputTypes`` in that
+file and ``key`` is the value you set for ``title`` in ``gBuiltInInputs``.
+You should request approval from UX before changing the default set of inputs.
+
+.. _here: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/widget/cocoa/nsTouchBar.mm#100
+
+If you are interested in adding new features to Firefox's implementation of the
+Touch Bar API, read on!
+
+
+Cocoa API
+~~~~~~~~~
+Firefox implements Apple's Touch Bar API in its Widget: Cocoa code with an
+``nsTouchBar`` class. ``nsTouchBar`` interfaces between Apple's Touch Bar API
+and the ``TouchBarHelper`` JavaScript API.
+
+The best resource to understand the Touch Bar API is Apple's
+`official documentation`_. This documentation will cover how Firefox implements
+these APIs and how one might extend ``nsTouchBar`` to enable new Touch Bar
+features.
+
+Every new Firefox window initializes ``nsTouchBar`` (link_). The function
+``makeTouchBar`` is looked for automatically on every new instance of an
+``NSWindow*``. If ``makeTouchBar`` is defined, that window will own a new
+instance of ``nsTouchBar``.
+
+At the time of this writing, every window initializes ``nsTouchBar`` with a
+default set of inputs. In the future, Firefox windows other than the main
+browser window (such as the Library window or DevTools) may initialize
+``nsTouchBar`` with a different set of inputs.
+
+``nsTouchBar`` has two different initialization methods: ``init`` and
+``initWithInputs``. The former is a convenience method for the latter, calling
+``initWithInputs`` with a nil argument. When that happens, a Touch Bar is
+created containing a default set of inputs. ``initWithInputs`` can also take an
+``NSArray<TouchBarInput*>*``. In that case, a non-customizable Touch Bar will be
+initialized with only those inputs available.
+
+.. _official documentation: https://developer.apple.com/documentation/appkit/nstouchbar?language=objc
+.. _link: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/widget/cocoa/nsCocoaWindow.mm#2877
+
+NSTouchBarItemIdentifiers
+-------------------------
+The architecture of the Touch Bar is based largely around an ``NSString*``
+wrapper class called ``NSTouchBarItemIdentifier``. Every input in the Touch Bar
+has a unique ``NSTouchBarItemIdentifier``. They are structured in reverse-URI
+format like so:
+
+``com.mozilla.firefox.touchbar.[TYPE].[KEY]``
+
+[TYPE] is a string indicating the type of the input, e.g. "button". If an
+input is a child of another input, the parent's type is prepended to the child's
+type, e.g. "scrubber.button" indicates a button contained in a scrubber.
+
+[KEY] is the ``title`` attribute defined for that input on the JS side.
+
+If you need to generate an identifier, use the convenience method
+``[TouchBarInput nativeIdentifierWithType:withKey:]``.
+
+.. caution::
+
+ Do not create a new input that would have the same identifier as any other
+ input. All identifiers must be unique.
+
+.. warning::
+
+ ``NSTouchBarItemIdentifier`` `is used in one other place`_: setting
+ ``customizationIdentifier``. Do not ever change this string. If it is changed,
+ any customizations users have made to the layout of their Touch Bar in Firefox
+ will be erased.
+
+Each identifier is tied to a ``TouchBarInput``. ``TouchBarInput`` is a class
+that holds the properties specified for each input in ``gBuiltInInputs``.
+``nsTouchBar`` uses them to create instances of ``NSTouchBarItem``
+which are the actual objects used by Apple's Touch Bar API and displayed in the
+Touch Bar. It is important to understand the difference between
+``TouchBarInput`` and ``NSTouchBarItem``!
+
+.. _is used in one other place: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/widget/cocoa/nsTouchBar.mm#71
+
+TouchBarInput creation flow
+---------------------------
+Creating a Touch Bar and its ``TouchBarInputs`` flows as follows:
+
+#. ``[nsTouchBar init]`` is called from ``[NSWindow makeTouchBar]``.
+
+#. ``init`` populates two NSArrays: ``customizationAllowedItemIdentifiers`` and
+ ``defaultItemIdentifiers``. It also initializes a ``TouchBarInput`` object
+ for every element in the union of the two arrays and stores them in
+ ``NSMutableDictionary<NSTouchBarItemIdentifier, TouchBarInput*>* mappedLayoutItems``.
+
+#. ``touchBar:makeItemForIdentifier:`` is called for every element in the union
+ of the two arrays of identifiers. This method retrieves the ``TouchBarInput``
+ for the given identifier and uses it to initialize a ``NSTouchBarItem``.
+ ``touchBar:makeItemForIdentifier:`` reads the ``type`` attribute from the
+ ``TouchBarInput`` to determine what ``NSTouchBarItem`` subclass should be
+ initialized. Our Touch Bar code currently supports ``NSCustomTouchBarItem``
+ (buttons, main buttons); ``NSPopoverTouchBarItem`` (popovers);
+ ``NSTextField`` (labels); and ``NSScrollView`` (ScrollViews).
+
+#. Once the ``NSTouchBarItem`` is initialized, its properties are populated with
+ an assortment of "update" methods. These include ``updateButton``,
+ ``updateMainButton``, ``updateLabel``, ``updatePopover``, and
+ ``updateScrollView``.
+
+#. Since the localization of ``TouchBarInput`` titles happens asynchronously in
+ JavaScript code, the l10n callback executes
+ ``[nsTouchBarUpdater updateTouchBarInputs:]``. This method reads the
+ identifier of the input(s) that need to be updated and calls their respective
+ "update" methods. This method is most often used to update ``title`` after
+ l10n is complete. It can also be used to update any property of a
+ ``TouchBarInput``; for instance, one might wish to change ``color``
+ when a specific event occurs in the browser.