yast/yast-yast2

View on GitHub
library/general/src/lib/ui/wizards/layout.rb

Summary

Maintainability
A
1 hr
Test Coverage
# Copyright (c) [2020] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast"

module UI
  module Wizards
    # Class to configure a wizard layout
    #
    # There are four possible layouts:
    #
    # * With a left sidebar which is usually used to place the installation steps.
    # * With a left tree.
    # * Without a sidebar/tree and with the title of the dialogs on the left.
    # * Without a sidebar/tree and with the title of the dialogs on top.
    #
    # Moreover, for each layout, a top banner can be added.
    #
    # @example
    #   layout1 = UI::Wizards::Layout.with_steps
    #   layout2 = UI::Wizards::Layout.with_title_on_left
    #   layout3 = UI::Wizards::Layout.with_title_on_top
    #
    #   layout3.show_banner
    #
    #   layout1.open_wizard do
    #     # ...
    #   end
    class Layout
      Yast.import "Wizard"
      Yast.import "ProductFeatures"

      # Class to represent a layout mode
      class Mode
        class << self
          # Mode for a layout with a left sidebar
          #
          # @return [Mode]
          def steps
            @steps ||= new(:steps)
          end

          # Mode for a layout with a left tree
          #
          # @return [Mode]
          def tree
            @tree ||= new(:tree)
          end

          # Mode for a layout without a sidebar/tree and with the title of the dialogs on the left
          #
          # @return [Mode]
          def title_on_left
            @title_on_left ||= new(:title_on_left)
          end

          # Mode for a layout without a sidebar/tree and with the title of the dialogs on top
          #
          # @return [Mode]
          def title_on_top
            @title_on_top ||= new(:title_on_top)
          end
        end

        # Whether the layout mode is for steps
        #
        # @return [Boolean]
        def steps?
          self == self.class.steps
        end

        # Whether the layout mode is for a tree
        #
        # @return [Boolean]
        def tree?
          self == self.class.tree
        end

        # Whether the layout mode is for title on left
        #
        # @return [Boolean]
        def title_on_left?
          self == self.class.title_on_left
        end

        # Whether the layout mode is for title on top
        #
        # @return [Boolean]
        def title_on_top?
          self == self.class.title_on_top
        end

      private

        # Constructor
        #
        # @param Mode [Symbol]
        def initialize(mode)
          @mode = mode
        end
      end

      class << self
        # Creates a new layout with a left sidebar
        #
        # @return [Layout]
        def with_steps
          new(Mode.steps)
        end

        # Creates a new layout with a left tree
        #
        # @return [Layout]
        def with_tree
          new(Mode.tree)
        end

        # Creates a new layout without a sidebar/tree and with the title of the dialogs on the left
        #
        # @return Layout]
        def with_title_on_left
          new(Mode.title_on_left)
        end

        # Creates a new layout without a sidebar/tree and with the title of the dialogs on top
        #
        # @return [Layout]
        def with_title_on_top
          new(Mode.title_on_top)
        end

        # Creates a new layout according to the product features
        #
        # @return [Layout]
        def from_product_features
          new.send(:load_product_features)
        end
      end

      # Layout mode
      #
      # @return [Mode]
      attr_reader :mode

      # Configures the layout to show a banner
      def show_banner
        @banner = true
      end

      # Configures the layout to not show a banner
      def hide_banner
        @banner = false
      end

      # Whether the layout includes a banner
      #
      # @return [Boolean]
      def banner?
        @banner
      end

      # Opens a new wizard according to the layout configuration
      #
      # @yield Code to run after opening the wizard. The wizard is automatically closed.
      def open_wizard(&block)
        Yast::Wizard.OpenWithLayout(self)

        return unless block_given?

        block.call
        close_wizard
      end

      # Closes the wizard
      def close_wizard
        Yast::Wizard.CloseDialog
      end

    private

      # Constructor
      #
      # @param mode [Mode] layout mode
      def initialize(mode = Mode.title_on_top)
        @mode = mode
        @banner = false
      end

      # Configures the layout according to the product features.
      #
      # Normally, the product features take the layout seetings from the globals section of a control
      # file, for example:
      #
      #   <globals>
      #     <installation_ui>sidebar</installation_ui>
      #     <installation_layout>
      #       <mode>steps</mode>
      #       <banner>true</banner
      #     </installation_layout>
      #   </globals>
      #
      # Note that installation_ui is deprecated in favor of installation_layout. In fact,
      # installation_layout takes precedence.
      #
      # @return [Layout]
      def load_product_features
        if installation_layout && !installation_layout.empty?
          @mode = installation_layout[:mode] unless installation_layout[:mode].nil?
          @banner = installation_layout[:banner] unless installation_layout[:banner].nil?
        elsif installation_ui == "sidebar"
          @mode = Mode.steps
          @banner = false
        else
          # Current default values when nothing is indicated in the control file
          @mode = Mode.title_on_left
          @banner = true
        end

        self
      end

      # Returns the value for the installation_ui setting from the product features
      #
      # This setting is only meaningful when its value is "sidebar". In that case, the layout should be
      # configured with the sidebar and without banner. See {#load_product_features}.
      #
      # Note that installation_ui is deprecated in favor of installation_layout.
      #
      # @return [String, nil]
      def installation_ui
        @installation_ui ||= Yast::ProductFeatures.GetFeature("globals", "installation_ui")
      end

      # Returns the values for the installation_layout setting from the product features
      #
      # @return [nil, Hash{Symbol => Mode, Boolean, nil}] e.g., { mode: Mode.steps, banner: true}.
      #   Returns nil when no settings are indicated for installation_layout.
      def installation_layout
        return @installation_layout unless @installation_layout.nil?

        installation_layout = Yast::ProductFeatures.GetFeature("globals", "installation_layout")

        return nil unless installation_layout.is_a?(Hash)

        @installation_layout = {}

        @installation_layout[:mode] =
          case installation_layout["mode"]
          when "steps"
            Mode.steps
          when "title-on-left"
            Mode.title_on_left
          when "title-on-top"
            Mode.title_on_top
          end

        @installation_layout[:banner] = installation_layout["banner"]

        @installation_layout
      end
    end
  end
end