How can I create a hidden button in Shoes? - ruby

In Shoes, I'd like to create a button that is initially hidden. I've tried passing :hidden => true as part of the button style, as well as calling #button.hide() after creating it, but it remains obstinately visible.
I've been able to work around this by not creating the button until I want it shown, but that requires checking to see if it already exists, rather than just using it.

Not at present. Buttons are still fairly unreliable in Shoes, especially on Windows. You can work around the issue by putting the button in a slot and hiding or showing the slot, but you may discover that the button won't hide again once it has been clicked once:
Shoes.app do
para 'This is some text.'
#flow = flow :width => 50, :hidden => true do
button 'a' do |btn|
alert 'Hello, World!'
end
end
button 'toggle' do
#flow.toggle
end
para 'Blah blah blah'
end
Luckily, there is a way out: slots. Slots can be given a click event, which makes them behave much as a button does. In fact, you can make fairly decent buttons out of slots.
Here's something I cobbled together. It lets you use the pesto_button method to generate buttons built on flows. You can modify it to fit your needs, including such things as using an image as the background, modifiable text (with auto-expanding width?), ability to change styles on the fly, etc:
class PestoButton < Widget
def initialize (text, opts = {})
#border_color = opts[:border_color] || gray
#border_width = opts[:border_width] || 3
#color = opts[:up_color] || gainsboro
#click_color = opts[:down_color] || #border_color
#width = opts[:width] || 80
#click = block_given? ? Proc.new { yield } : nil
#text = text
#visible = true
#flow = flow :width => #width do
background #color
border #border_color, :strokewidth => #border_width
para #text, :align => 'center'
end
#flow.click do
#flow.clear
#flow.append do
background #click_color
border #border_color, :strokewidth => #border_width
para #text, :align => 'center'
end
end
#flow.release do
#flow.clear
#flow.append do
background #color
border #border_color, :strokewidth => #border_width
para #text, :align => 'center'
#click.call if #click
end
end
end
def click
#click = block_given? ? Proc.new { yield } : nil
end
def show
#flow.show
end
def toggle
#flow.toggle
end
def hide
#flow.hide
end
end
Shoes.app do
para 'This is some text.'
#btn = pesto_button 'Click me!' do
alert 'Hello, World!'
end
button 'toggle' do
#btn.toggle
end
button 'new block' do
#btn.click do
alert 'Goodbye, World!'
end
end
button 'no block' do
#btn.click #Clears the click method
end
para 'Blah blah blah'
end

Related

Refresh ruby stack with Shoes

this can be a very simple question but I have been searching for it a long time and haven't found any valid answer yet...
I am try to do a Shoes app with ruby to get a list of names and save it in a file. I would like to show the list that have been introduced already and as a new name enters the list the list which is shown would be updated. Also, I would like to associate a delete button to each element of the list so the user can remove that name if needed.
After all this, the only thing that I can get is the part in which you add a name and it gets saved into a list but adding this second stack with a list of all names with a delete button... not so lucky. This is how my code looks like, the functions addName and saveFile are not here but they only do some work with the list. Also, some global variables and constat definitions are not shown:
Shoes.app(title: "My Higgs field!",
width: APPW, height: APPH, resizable: false) {
stack{
flow{
#edit = edit_line
#enter = button "Add"
#enter.click do
info "Enter to add #{#edit.text}"
addName(#edit.text)
end
}
#save_file = button "Guardar ficheiro"
#save_file.click do
saveFile
end
}
stack do
# Show a list with all the names inserted
$names.each do |name|
flow{
#line = para name
info "Putting line #{#line}"
#delete = button "Apagar"
#delete.click do
deleteName name
end
}
end
end
}
Any inputs on this is very wellcome!
Thanks a lot!
A shoes question so long not answered ! That can't be.
Here a fully working version, i took the liberty to reorganise everything and use my favorite shoes color: green
require 'green_shoes'
Shoes.app(title: "My Higgs field!", width: 200, height: 200, resizable: false) do
#names, #list = [], nil
def update
#list.clear()
#names.each do |name|
#list.append do
flow do
para link('del'){del(name); update}, ' ', name
end
end
end
end
def add name
#names << name
#names = #names.uniq.sort
update
end
def del name
#names.delete(name)
update
end
stack do
flow do
#edit = edit_line(width: 110, height: 23)
keypress do|k|
if k == "\n"
add(#edit.text)
end
end
button "Add" do
add(#edit.text)
end
button "Save file" do
File.open("names.txt", "w+") { |file| file.write(#names.join("\n")) }
alert("names.txt is saved")
end
end
#list = flow do
#names.each do |p|
para p, :size => 10
end
end
end
end

Controlling content flow with Prawn

Let's say we want to display a title on the first page that takes up the top half of the page. The bottom half of the page should then fill up with our article text, and the text should continue to flow over into the subsequent pages until it runs out:
This is a pretty basic layout scenario but I don't understand how one would implement it in Prawn.
Here's some example code derived from their online documentation:
pdf = Prawn::Document.new do
text "The Prince", :align => :center, :size => 48
text "Niccolò Machiavelli", :align => :center, :size => 20
move_down 42
column_box([0, cursor], :columns => 3, :width => bounds.width) do
text((<<-END.gsub(/\s+/, ' ') + "\n\n") * 20)
All the States and Governments by which men are or ever have been ruled,
have been and are either Republics or Princedoms. Princedoms are either
hereditary, in which the bla bla bla bla .....
END
end
end.render
but that will just continue to show the title space for every page:
What's the right way to do this?
I have been fighting with this same problem. I ended up subclassing ColumnBox and adding a helper to invoke it like so:
module Prawn
class Document
def reflow_column_box(*args, &block)
init_column_box(block) do |parent_box|
map_to_absolute!(args[0])
#bounding_box = ReflowColumnBox.new(self, parent_box, *args)
end
end
private
class ReflowColumnBox < ColumnBox
def move_past_bottom
#current_column = (#current_column + 1) % #columns
#document.y = #y
if 0 == #current_column
#y = #parent.absolute_top
#document.start_new_page
end
end
end
end
end
Then it is invoked exactly like a normal column box, but on the next page break will reflow to the parents bounding box. Change your line:
column_box([0, cursor], :columns => 3, :width => bounds.width) do
to
reflow_column_box([0, cursor], :columns => 3, :width => bounds.width) do
Hope it helps you. Prawn is pretty low level, which is a two-edged sword, it sometimes fails to do what you need, but the tools are there to extend and build more complicated structures.
I know this is old, but I thought I'd share that a new option has been added to fix this in v0.14.0.
:reflow_margins is an option that sets column boxes to fill their parent boxes on new page creation.
column_box(reflow_margins: true, columns: 3)
So, the column_box method creates a bounding box. The documented behavior of the bounding box is that it starts at the same position as on the previous page if it changes to the next page. So the behavior you are seeing is basically correct, also not what you want. The suggested workaround I have found by googling is to use a span instead, because spans do not have this behavior.
The problem now is, how to build text columns with spans? They don't seem to support spans natively. I tried to build a small script that mimicks columns with spans. It creates one span for each column and aligns them accordingly. Then, the text is written with text_box, which has the overflow: :truncate option. This makes the method return the text that did not fit in the text box, so that this text can then be rendered in the next column. The code probably needs some tweaking, but it should be enough to demonstrate how to do this.
require 'prawn'
text_to_write = ((<<-END.gsub(/\s+/, ' ') + "\n\n") * 20)
All the States and Governments by which men are or ever have been ruled,
have been and are either Republics or Princedoms. Princedoms are either
hereditary, in which the bla bla bla bla .....
END
pdf = Prawn::Document.generate("test.pdf") do
text "The Prince", :align => :center, :size => 48
text "Niccolò Machiavelli", :align => :center, :size => 20
move_down 42
starting_y = cursor
starting_page = page_number
span(bounds.width / 3, position: :left) do
text_to_write = text_box text_to_write, at: [bounds.left, 0], overflow: :truncate
end
go_to_page(starting_page)
move_cursor_to(starting_y)
span(bounds.width / 3, position: :center) do
text_to_write = text_box text_to_write, at: [bounds.left, 0], overflow: :truncate
end
go_to_page(starting_page)
move_cursor_to(starting_y)
span(bounds.width / 3, position: :right) do
text_box text_to_write, at: [bounds.left, 0]
end
end
I know this is not an ideal solution. However, this was the best I could come up with.
Use floats.
float do
span((bounds.width / 3) - 20, :position => :left) do
# Row Table Code
end
end
float do
span((bounds.width / 3) - 20, :position => :center) do
# Row Table Code
end
end
float do
span((bounds.width / 3) - 20, :position => :right) do
# Row Table Code
end
end
Use Prawns grid layout instead. It is very well documented...and easier to control your layout.

Prawn doesn't seem to push layout down when using repeat(:all)

I am generating a document with data that flows onto each subsequent page, each page has a standard header. However, when I use repeat(:all) to put the header on each page, I find that on every page but the first page, the next content is not being moved down by the size of the header banner I have put on the page.
My code for generating the banner:
class SmartsoftPdf < Prawn::Document
BOX_MARGIN = 30
RHYTHM = 10
INNER_MARGIN = 30
# Colors
#
BLACK = "000000"
LIGHT_GRAY = "F2F2F2"
GRAY = "DDDDDD"
DARK_GRAY = "333333"
BROWN = "A4441C"
ORANGE = "F28157"
LIGHT_GOLD = "FBFBBE"
DARK_GOLD = "EBE389"
BLUE = "08C"
GREEN = "00ff00"
RED = "ff0000"
def show_header(text,date)
header_box do
image "#{Rails.root}/app/assets/images/smart_records_logo_h60.png", :height => 40
draw_text text,
:at => [80,25], :size => 12, :style => :bold, :color => BLUE
draw_text "Date: #{ausDate(date)}",
:at => [bounds.right - 100,bounds.top - 15], :size => 10 if date
end
end
def header_box(&block)
bounding_box([-bounds.absolute_left, cursor + BOX_MARGIN + 8],
:width => bounds.absolute_left + bounds.absolute_right,
:height => BOX_MARGIN*2) do
fill_color LIGHT_GRAY
fill_rectangle([bounds.left, bounds.top],
bounds.right,
bounds.top - bounds.bottom)
fill_color BLACK
move_down(RHYTHM)
indent(BOX_MARGIN, &block)
end
stroke_color GRAY
stroke_horizontal_line(-BOX_MARGIN, bounds.width + BOX_MARGIN, :at => cursor)
stroke_color BLACK
move_down(RHYTHM*4)
end
end
Then within the pdf generation itself I do:
repeat(:all) do
show_header("Custom Report",DateTime.now())
end
However, when I start putting content onto the pages, I expect when the content overflows onto the next page that the content will show up after the header. I'm finding that the header overlaps the content instead.
Here is an image which illustrates the problem: http://i.imgur.com/mSy2but.png
Am I building the header box incorrectly? Do I need to do something additional to make it so that the content which spills into the next page gets pushed down the appropriate amount?
Okay. I have solved this myself. Most recent version of Prawn has a better way to handle this case. When you use repeat(:all) the page is reopened AFTER document creation and your content creation items are then added. This doesn't push the page down. The correct way to add this header to every page is to use the "canvas" method which allows you to operate out of the bounds of the page margin. Use canvas to draw a box at the top of the page, and set the top_margin of the page to push all content below the banner.
canvas do
bounding_box([bounds.left,bounds.top],
:width => bounds.absolute_left + bounds.absolute_right,
:height => BOX_MARGIN*2) do
fill_color LIGHT_GRAY
fill_rectangle([bounds.left, bounds.top],
bounds.right,
bounds.top - bounds.bottom)
fill_color BLACK
move_down(RHYTHM)
indent(BOX_MARGIN, &block)
end
stroke_color GRAY
stroke_horizontal_line(-BOX_MARGIN, bounds.width + BOX_MARGIN, :at => cursor)
stroke_color BLACK
end
at document creation...
def initialize(options = {})
super(:page_layout => :landscape,:top_margin => HEIGHT_OF_BANNER)
end

How can I display the output of an array in Ruby Shoes?

I've got this as my code
openAll = File.open('N:\Josh\Blondie\db.txt')
allNumbers = Array.new
allNumbers=[]
openAll.each_line {|line|
allNumbers.push line
}
puts allNumbers
and I'd like to be able to display the output of this code in a new window with Ruby Shoes, I can't seem to get it to display anything though. The contents of the file are names and phone numbers.
Any ideas?
Here's an example of outputting text to a shoes window. Using a puts statement just outputs to the shell, not to the Shoes app.
Shoes.app :title => "GUI RAW file converter, for the CLI challenged",
:resizable => true do
background white
stack do
flow {
background gray, :height => 30
caption "Caption", :margin => 8, :stroke => white
stack {
para 'This is a fancy line I just printed to the window'
####### Here's an example line you could use to put out the array...
allNumbers.each do |number|
para "#{number}"
end
}
}
end
end
I guess you should use the method Kernel#alert instead of Kernel#puts.
http://shoesrb.com/manual/Built-in.html

Ruby Shoes - Problem with edit list and if-statement

I want to run a program which changes the background depending on which gender you choose in a edit list.
Shoes.app do
para "your gender"
list_box :items => ["female", "male"],
width => 120, :choose => "weiblich" do |list|
#gender.text = list.text
end
#gender = para "#{#gender}"
button "change colours" do
if #gender ="female"
background (deeppink)
else #gender ="male"
background (dodgerblue)
end
end
end
The problem is - whatever I do, if I use the if-statement, suddenly always "female" is in the variable and my background is pink, altough when I pick "male". If I just do
...
button "change colours" do
para #gender
end
....
the right gender is in the variable #gender. Does anybody know what the problem is?
You need if #gender == "female" and if #gender == "male" - note the two = symbols.
You're performing an assignment, not testing equality.

Resources