OmniOutliner Styles: An Exercise in Useless Frustration

While I am a major cheerleader for OmniOutliner, there is one area that is a source for endless frustration for me. I am working on a font inventory for my iMac and I am forced to apply styles on a block-by-block basis. For instance, I have Level 1 Rows defined with Avenir Next Thin 16 pt. The problem is that for the last two columns in a Level 1 row, I want to have a different appearance. The columns styles are defined as,

Classification(s) Avenir Next Thin 12 pt
Copyright(s) Avenir Next Regular 11 pt

The problem is that the column styles are always de-prioritized because the row style always takes precedence and there is no way to change the order for styles. Thus, in what will ultimately be a large document, I am being slowed down because OmniOutliner is dictating style priorities instead of letting me to define where and how styles are applied.

Named styles are also hit or miss. Just getting the attributes right for a named style seems to be inconsistent; I am witnessing the named style becoming deselected each time an attribute is modified. Applying a named style to a selection seems to easily fall prey to the same behavior I see with the document styles, where the named style applied to a selection is superseded by the row style.

While it looks good, OmniOutliner Help, offers little information. The help document is not searchable, and when the user happens upon the topic they wish to explore, the details are at best spartan; if any information is provided at all. In The Omni Group’s defense, much of OmniOutliner’s features are self-explanatory, but when I actually need to look something up, OmniOutliner Help seems to just gloss over many topics instead of providing in-depth details.

1 Like

So, I was finally able to get the document styles to work properly (for what I am doing). The downside is that I was doing so much firing in anger that I am not sure of what actions led to the document now behaving as I wish. Part of the issue is the way in which styles are applied in OmniOutliner. The standard for multiple applied styles is what one would see in Microsoft Word or Apple Pages—it may also be the case in some graphics software that permit user-defined styles—where there are only two classes of styles.

In Word and Pages, paragraph styles serve as the basis for a paragraph, while character styles supplement or replace the paragraph style for a subset of text in a paragraph. OmniOutliner permits applying numerous styles, and therefore, should have a level style management that is absent, and not necessary, in other software. To that end, either the application of styles need to be detailed much better in OmniOutliner Help, or better, the user should be able to modify the order in which styles are applied.

Another thing that frustrates me is that there’s no way to apply checkboxes to only a part of an outline - it’s all or nothing. I have large outlines covering all aspects of projects and would like only a few selected topics and their subtopics to have checkmarks, as to-do lists. Filed a feature request ages ago, but this product seems to get little love; OF gets all the attention.

People have been asking for usable checkboxes for years without success, or even much of a response. At this point, I’d guess that they’ve decided against it, perhaps because it would give OO a functionality that’s currently only available in OO. At least for me, checkboxes in OO wouldn’t cause me to drop OF, it would just make OO usable in a much wider range of problems.

The checkbox issue is dependent upon how you use them. Based on what I have seen on these boards, the all or nothing is associated with Status Checkboxes; the checkboxes that appear ahead of each entry in the first column of an OmniOutliner document. I can think of a few use cases where that functionality would be preferable.

Column Type checkboxes include an extra layer of control by selecting one of three summary types. Choosing the Hidden column type only shows checkboxes at the lowest row level with the summary in higher level rows hidden. Unfortunately, when applied to the Topic column, the column will only have the checkbox with no accompanying text.

All software have idiosyncrasies and I will be the first to admit that OmniOutliner is, in my opinion, much better than other outlining software that I have tried out; particularly those for Windows. Most of my structured documents are easily handled by OmniOutliner, but it is large documents like the font inventory that I am currently working on that tend to have formatting issues due to how I want information to be presented.

Perhaps a solution would be to create a set of Omni Automation actions that would apply styles in the way you’d like.

The section on Styles covers creating and applying the various kinds of styles:

https://omni-automation.com/omnioutliner/style.html

The Text section covers text objects which are styled text ranges within an OmniOutliner item.

https://omni-automation.com/omnioutliner/text.html

For example, this page shows how to insert/remove styled text markers to/from the start of rows:

https://omni-automation.com/omnioutliner/text-object-01.html

Fairly arduous, particularly in that coding style.

If you were going to apply styles from omniJS, there would be less typing and more room for flexibility if the name ⇄ value pairs were just kept as a JavaScript dictionary:

Instead of:

Typing-intensive code shown on omni-automation style page
aStyle.set(Style.Attribute.FontCondensed, false)
02		aStyle.set(Style.Attribute.FontFamily, 'Helvetica Neue')
03		aStyle.set(Style.Attribute.FontFillColor, Color.white)
04		aStyle.set(Style.Attribute.FontFixedPitch, false)
05		aStyle.set(Style.Attribute.FontItalic, true)
06		aStyle.set(Style.Attribute.FontName, 'Helvetica Neue Medium Italic')
07		aStyle.set(Style.Attribute.FontNarrow, false)
08		aStyle.set(Style.Attribute.FontSize, 14)
09		aStyle.set(Style.Attribute.FontStrokeColor, Color.red)
10		aStyle.set(Style.Attribute.FontStrokeWidth, 2)
11		aStyle.set(Style.Attribute.FontWeight, 6)

A simpler and more readable JS ‘dictionary’ of key-value pairs would be:

const styleProperties = {
    FontCondensed: false,
    FontFamily: 'Helvetica Neue',
    FontFillColor: Color.white,
    FontFixedPitch: false,
    FontItalic: true,
    FontName: 'Helvetica Neue Medium Italic',
    FontNarrow: false,
    FontSize: 28,
    FontStrokeColor: Color.red,
    FontStrokeWidth: 2,
    FontWeight: 6
};

and a reusable (copy/pasted) helper function could directly apply all these settings to the the styles of selected items.

The reusable helper function might look like:

// GENERAL AND RE-USABLE HELPER FUNCTION
const setStyleProperties = dictKeyValues => styleObject => {
    Object.keys(dictKeyValues)
        .forEach(
            k => styleObject.set(
                Style.Attribute[k],
                dictKeyValues[k]
            )
        )
};

and returning to the suggestion:

There is a lot of fiddle and technical detail in setting those things up, not a low enough cost/benefit to be done very lightly or on the fly, and they are only really needed to cope with the limitations of iOS.

On macOS, much simpler and quicker to drop some code into a Keyboard Maestro ‘Execute JavaScript’ action, and assign a keystroke to it.

This, for example, could be run that way:

Sample macro - apply style in OmniOutliner.kmmacros.zip (10.6 KB)

(() => {
    'use strict';

    // Run from Script Editor
    //
    //      Or paste into a Keyboard Maestro Execute JXA action,
    //      and a assign a keystroke.
    //
    // ( Uses JavaScript for Automation to run an OmniJS main function
    //  in the active OmniOutliner document )

    // omniJS code to evaluate in active document.
    const omniJS = () => {
        const main = () => {
            const styleProperties = {
                FontCondensed: false,
                FontFamily: 'Helvetica Neue',
                FontFillColor: Color.white,
                FontFixedPitch: false,
                FontItalic: true,
                FontName: 'Helvetica Neue Medium Italic',
                FontNarrow: false,
                FontSize: 28,
                FontStrokeColor: Color.red,
                FontStrokeWidth: 2,
                FontWeight: 6
            };

            document.editors[0].selectedNodes.forEach(
                node => setStyleProperties(styleProperties)(
                    node.object.style
                )
            );
        };

        // GENERAL AND RE-USABLE HELPER FUNCTION
        const setStyleProperties = dictKeyValues => styleObject => {
            Object.keys(dictKeyValues)
                .forEach(
                    k => styleObject.set(
                        Style.Attribute[k],
                        dictKeyValues[k]
                    )
                )
        };

        // MAIN FUNCTION CALLED HERE.
        return main();
    };

    // JAVASCRIPT FOR AUTOMATION TO LAUNCH OMNI JS
    //  (evaluate this whole file in Script Editor with language tab
    //      at top left set to JavaScript)

    // outlineOmniJSWithArgs :: Function -> [...OptionalArgs] -> a
    function outlineOmniJSWithArgs(f) {
        return Application('omniOutliner')
            .evaluateJavascript(
                `(${f})(${Array.from(arguments)
                .slice(1).map(JSON.stringify)})`
            );
    };

    return 0 < Application('OmniOutliner').documents.length ? (
        outlineOmniJSWithArgs(omniJS)
    ) : 'No documents open in OmniOutliner.'
})();