chromium/chrome/browser/ui/cocoa/main_menu_builder.h

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_COCOA_MAIN_MENU_BUILDER_H_
#define CHROME_BROWSER_UI_COCOA_MAIN_MENU_BUILDER_H_

#import <Cocoa/Cocoa.h>

#include <optional>
#include <ostream>
#include <string>
#include <vector>

#include "base/check_op.h"

namespace chrome {

// Creates the main menu bar using the name specified in |product_name|, and
// registers it as such on |nsapp|. The NSApplicationDelegate |app_delegate|
// is the target for specific, special menu items.
//
//
// Normally the main menu is built in a MainMenu.nib file, but NIB files files
// are hard to edit (especially cross-platform) and bring in a compile
// dependency on ibtool. Building the menu in code has a lower maintenance
// burden.
void BuildMainMenu(NSApplication* nsapp,
                   id<NSApplicationDelegate> app_delegate,
                   const std::u16string& product_name,
                   bool is_pwa);

NSMenuItem* BuildFileMenuForTesting(bool is_pwa);

// Internal ////////////////////////////////////////////////////////////////////

namespace internal {

// Helper class that builds NSMenuItems from data.
//
// This builder follows a fluent-interface pattern where the setters are
// not prefixed with the typical "set_" and they return a reference to this
// for easier method chaining.
//
// This class is only exposed for testing.
class MenuItemBuilder {
 public:
  explicit MenuItemBuilder(int string_id = 0);

  MenuItemBuilder(const MenuItemBuilder&);
  MenuItemBuilder& operator=(const MenuItemBuilder&);

  ~MenuItemBuilder();

  // Converts the item to a separator. Only tag() and hidden() are also
  // applicable.
  MenuItemBuilder& is_separator() {
    DCHECK_EQ(string_id_, 0);
    is_separator_ = true;
    return *this;
  }

  MenuItemBuilder& target(id target) {
    target_ = target;
    return *this;
  }

  MenuItemBuilder& action(SEL action) {
    DCHECK(!action_);
    action_ = action;
    return *this;
  }

  MenuItemBuilder& tag(int tag) {
    tag_ = tag;
    return *this;
  }

  // Wires up the menu item to the CommandDispatcher based on an
  // IDC_ command code.
  MenuItemBuilder& command_id(int command_id) {
    return tag(command_id).action(@selector(commandDispatch:));
  }

  // Specifies the string to substitute for the $1 found in the string for
  // |string_id_|.
  MenuItemBuilder& string_format_1(const std::u16string& arg) {
    string_arg1_ = arg;
    return *this;
  }

  MenuItemBuilder& submenu(std::vector<MenuItemBuilder> items) {
    submenu_ = std::move(items);
    return *this;
  }

  // Registers a custom key equivalent. Normally the key equivalent is looked
  // up via AcceleratorsCocoa based on the command_id(). If one is not present,
  // the one specified here is used instead.
  MenuItemBuilder& key_equivalent(NSString* key_equivalent,
                                  NSEventModifierFlags flags) {
    CHECK((flags & NSEventModifierFlagShift) == 0)
        << "The shift modifier flag should be directly applied to the key "
           "equivalent.";
    key_equivalent_ = key_equivalent;
    key_equivalent_flags_ = flags;
    return *this;
  }

  // Marks the item as an alternate keyboard equivalent menu item.
  MenuItemBuilder& is_alternate() {
    is_alternate_ = true;
    return *this;
  }

  // Excludes this item from the menu if |condition| is true.
  MenuItemBuilder& remove_if(bool condition) {
    is_removed_ |= condition;
    return *this;
  }

  // Hide this item from the menu if |condition| is true.
  MenuItemBuilder& set_hidden(bool condition) {
    is_hidden_ |= condition;
    return *this;
  }

  // Marks the item as a section header menu item.
  MenuItemBuilder& is_section_header() {
    is_section_header_ = true;
    return *this;
  }

  // Builds a NSMenuItem instance from the properties set on the Builder.
  NSMenuItem* Build() const;

 private:
  bool is_separator_ = false;

  int string_id_ = 0;
  std::u16string string_arg1_;

  int tag_ = 0;

  id __strong target_ = nil;
  SEL action_ = nil;

  NSString* __strong key_equivalent_ = @"";
  NSEventModifierFlags key_equivalent_flags_ = 0;

  bool is_alternate_ = false;

  bool is_removed_ = false;

  std::optional<std::vector<MenuItemBuilder>> submenu_;

  bool is_hidden_ = false;

  bool is_section_header_ = false;
};

}  // namespace internal
}  // namespace chrome

#endif  // CHROME_BROWSER_UI_COCOA_MAIN_MENU_BUILDER_H_