Skip to content

Commit

Permalink
Merge pull request #608 from yast/caasp_merge
Browse files Browse the repository at this point in the history
Caasp merge
  • Loading branch information
jreidinger committed Aug 9, 2017
2 parents aa2fa0c + 23df941 commit 7e83f93
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 26 deletions.
65 changes: 65 additions & 0 deletions library/cwm/examples/replace_point_example.rb
@@ -0,0 +1,65 @@
# Simple example to demonstrate object oriented replace_point widget

require_relative "example_helper"

require "yast"

require "cwm/widget"

Yast.import "UI"
Yast.import "CWM"
Yast.import "Wizard"

class SwitchWidget < CWM::PushButton
def initialize(replace_point, widgets)
@replace_point = replace_point
@widgets = widgets
end

def label
"Switch"
end

def handle
@widgets.rotate!
@replace_point.replace(@widgets.first)
end
end

class PopupButtonWidget < CWM::PushButton
def label
"Popup"
end

def handle
Yast::Popup.Message("Click!")
end
end

class StoreWidget < CWM::InputField
def label
"write here"
end

def validate
return true unless value.empty?

Yast::Popup.Error("Empty value!")
false
end

def store
Yast::Popup.Message(value)
end
end

widgets = [PopupButtonWidget.new, CWM::Empty.new(:empty), StoreWidget.new]
replace_point = CWM::ReplacePoint.new(widget: widgets.first)

content = Yast::Term.new(:VBox,
SwitchWidget.new(replace_point, widgets),
replace_point)

Yast::Wizard.CreateDialog
Yast::CWM.show(content)
Yast::Wizard.CloseDialog
72 changes: 72 additions & 0 deletions library/cwm/src/lib/cwm/widget.rb
Expand Up @@ -821,4 +821,76 @@ def replace_point
ReplacePoint(Id(replace_point_id), VBox(VStretch(), HStretch()))
end
end

# Placeholder widget that is used to replace content on demand.
# The most important method is {#replace} which allows switching content
class ReplacePoint < CustomWidget
# @param id [Object] id of widget. Needed to redefine only if more than one
# placeholder needed to be in dialog. Parameter type is limited by component
# system
# @param widget [CWM::AbstractWidget] initial widget in placeholder
def initialize(id: "_placeholder", widget: Empty.new("_initial_placeholder"))
self.handle_all_events = true
self.widget_id = id
@widget = widget
end

def contents
ReplacePoint(Id(widget_id), widget_content(@widget))
end

def init
@widget.init if @widget.respond_to?(:init)
end

# Replaces content with different widget. All its events are properly
# handled.
# @param widget [CWM::AbstractWidget] widget to display and process events
def replace(widget)
log.info "replacing with new widget #{widget.inspect}"
Yast::UI.ReplaceWidget(Id(widget_id), widget_content(widget))
@widget = widget
init
end

def help
@widget.respond_to?(:help) ? @widget.help : ""
end

def handle(event)
return unless @widget.respond_to?(:handle)

if !@widget.handle_all_events
return if event["ID"] != @widget.widget_id
end

m = @widget.method(:handle)
if m.arity == 0
m.call
else
m.call(event)
end
end

def validate
@widget.respond_to?(:validate) ? @widget.validate : true
end

def store
@widget.store if @widget.respond_to?(:store)
end

def cleanup
@widget.cleanup if @widget.respond_to?(:cleanup)
end

private

def widget_content(widget)
definition = widget.cwm_definition
definition["_cwm_key"] = widget.widget_id # a bit hacky way to pass widget id
definition = Yast::CWM.prepareWidget(definition)
definition["widget"]
end
end
end
15 changes: 9 additions & 6 deletions library/cwm/src/modules/CWM.rb
Expand Up @@ -787,7 +787,8 @@ def handleDebug
# @param [Hash] functions map initialize/save/handle fallbacks if not specified
# with the widgets.
# @param [Array<Object>] skip_store_for list of events for which the value of the widget will not be stored
# Useful mainly for non-standard redraw of widgets, like :reset or :redraw
# Useful mainly for non-standard redraw of widgets, like :reset or :redraw. It will skip also
# validation, because it is not needed as nothing is stored.
# @return [Symbol] wizard sequencer symbol
def Run(widgets, functions, skip_store_for: [])
widgets = deep_copy(widgets)
Expand Down Expand Up @@ -855,11 +856,12 @@ def Run(widgets, functions, skip_store_for: [])

next if ret.nil?

ret = nil if save && !validateWidgets(widgets, event_descr)

if ret.nil?
# ok, so what happens here? event want to save widgets, so check that there is no explicit
# skip of storing for this event and there is a widget containing invalid value.
# In such case do not save and clear ret, so we are still in loop
if save && !skip_store_for.include?(ret) && !validateWidgets(widgets, event_descr)
ret = nil
save = false
next
end
end
saveWidgets(widgets, event_descr) if save && !skip_store_for.include?(ret)
Expand Down Expand Up @@ -928,7 +930,8 @@ def SetValidationFailedHandler(handler)
# @param [String] abort_button label for dialog abort button
# @param [Array] skip_store_for list of events for which the value of the widget will not be stored.
# Useful mainly when some widget returns an event that should not trigger the storing,
# like a reset button or a redrawing
# like a reset button or a redrawing. It will skip also validation, because it is not needed
# as nothing is stored.
# @return [Symbol] wizard sequencer symbol
def show(contents, caption: nil, back_button: nil, next_button: nil, abort_button: nil, skip_store_for: [])
widgets = widgets_in_contents(contents)
Expand Down
128 changes: 128 additions & 0 deletions library/cwm/test/widgets_test.rb
Expand Up @@ -213,3 +213,131 @@ def contents
end
end
end

describe CWM::ReplacePoint do

class ReplacePointTestWidget < CWM::InputField
def label
"test"
end

def init
end

def handle
end

def help
"help"
end

def validate
false
end

def store
end

def cleanup
end
end

describe ".new" do
it "has widget_id as passed" do
subject = described_class.new(id: "test")
expect(subject.widget_id).to eq "test"
end

it "uses passed widget as initial content" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(widget).to receive(:init)
subject.init
end
end

describe "#contents" do
it "generates contents including current widget UI definition" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)

expect(subject.contents).to eq(
ReplacePoint(
Id(subject.widget_id),
InputField(Id(widget.widget_id), Opt(:hstretch), "test")
)
)
end
end

describe "#init" do
it "passes init to enclosed widget" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(widget).to receive(:init)
subject.init
end
end

describe "#replace" do
it "changes enclosed widget" do
subject = described_class.new(widget: CWM::Empty.new(:initial))
widget = ReplacePointTestWidget.new
expect(widget).to receive(:store)
subject.replace(widget)
subject.store
end
end

describe "#help" do
it "returns help of enclosed widget" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(subject.help).to eq "help"
end
end

class ComplexHandleTest < CWM::Empty
def handle(_event)
nil
end
end

describe "#handle" do
# Cannot test arity based dispatcher, because if we mock expect call of widget.handle, it is
# replaced by rspec method with -1 arity, causing wrong dispatcher functionality

it "do nothing if passed event is not widget_id and enclosed widget do not handle all events" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(widget).to_not receive(:handle)
subject.handle("ID" => "Not mine")
end
end

describe "#validate" do
it "passes validate to enclosed widget" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(subject.validate).to eq false
end
end

describe "#store" do
it "passes store to enclosed widget" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(widget).to receive(:store)
subject.store
end
end

describe "#cleanup" do
it "passes cleanup to enclosed widget" do
widget = ReplacePointTestWidget.new
subject = described_class.new(widget: widget)
expect(widget).to receive(:cleanup)
subject.cleanup
end
end
end
3 changes: 2 additions & 1 deletion library/general/src/Makefile.am
Expand Up @@ -91,7 +91,8 @@ ylib2_DATA = \
lib/ui/dialog.rb \
lib/ui/installation_dialog.rb \
lib/ui/event_dispatcher.rb \
lib/ui/service_status.rb
lib/ui/service_status.rb \
lib/ui/widgets.rb

EXTRA_DIST = $(module_DATA) $(client_DATA) $(scrconf_DATA) $(agent_SCRIPTS) $(ydata_DATA) $(fillup_DATA) $(ylib_DATA) $(ylib2_DATA)

Expand Down
15 changes: 13 additions & 2 deletions library/general/src/lib/installation/proposal_client.rb
Expand Up @@ -116,6 +116,12 @@ def run
# submodules that translate any strings internally based on internal maps
# (e.g., keyboard etc.) need to take more action.
#
# @option attrs [Boolean] "simple_mode"
# Propose in a different format,
# pioneered in the CASP/CaaSP all-in-one dialog: we are short of space,
# and employ labels instead of RichText.
# The result must include `label_proposal`.
#
# @return [Hash] containing:
#
# * **`"links"`** [Array<String>] ---
Expand All @@ -126,12 +132,17 @@ def run
# Human readable proposal preformatted in HTML. It is possible to use
# the {Yast::HTMLClass Yast::HTML} module for such formatting.
#
# * **`"raw_proposal"`** [Array<String>, nil]
# (only used if `preformatted_proposal` is not present) ---
# * **`"raw_proposal"`** [Array<String>, nil] ---
# (only used if `preformatted_proposal` is not present)
# Human readable proposal, not formatted yet.
# The caller will format each item as a HTML list item (`<li>`). The
# proposal can contain hyperlinks with IDs listed in the list `links`.
#
# * **`"label_proposal"`** [Array<String>, nil] ---
# (only used for `simple_mode`)
# Human readable proposal.
# The caller will format each item as Label.
#
# * **`"warning"`** [String, nil] ---
# Warning in human readable format without HTML tags other than `\<br>`.
# The warning will be embedded in appropriate HTML format specifications
Expand Down
39 changes: 39 additions & 0 deletions library/general/src/lib/ui/widgets.rb
@@ -0,0 +1,39 @@
# encoding: utf-8

# ------------------------------------------------------------------------------
# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
#
#
# 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 Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail, you may find
# current contact information at www.novell.com.
# ------------------------------------------------------------------------------

require "yast"
require "cwm/widget"

module UI
module Widgets
# Widget representing input field for testing keyboard layout.
# Its value is ignored and never used anywhere.
class KeyboardLayoutTest < CWM::InputField
def initialize
textdomain "base"
end

def label
_("&Test Keyboard Layout")
end
end
end
end

0 comments on commit 7e83f93

Please sign in to comment.