Yes, Petunia! You CAN code a browser in Perl6.
UPDATED: You can also use JavaScript, and get the results back!
Yes, Petunia! You CAN code a browser in Perl6.
UPDATED: You can also use JavaScript, and get the results back!
Second script is a LOT longer:
use v6.c;
use GTK::Compat::Types;
use WebkitGTK::Raw::Types;
use GTK::Application;
use WebkitGTK::WebView;
use WebkitGTK::JavaScript::Context;;
my $a = GTK::Application.new(
title => 'org.genex.p6-browser', width => 640, height => 480
);
sub print_story_title($o, $r, $ud, $wv) {
my $js_result = $wv.run_javascript_finish($r);
without $js_result {
say "Error running javascript: { $ERROR.message }" with $ERROR;
return;
}
my $val = $js_result.get_js_value;
if $val.defined && $val.is_string {
my $s = $val.to_string;
my $e = WebkitGTK::JavaScript::Context.new(
$val.get_context
).get_exception;
with $e {
say "Error running javascript: { $e.get_message }";
} else {
say "First story title is: { $s }";
}
} else {
say 'Error running javascript: unexpected return value';
}
}
sub get_story_title($wv) {
$wv.run_javascript(
'$("header span.story-title a")[0].innerText;',
-> *@a {
CATCH { default { .message.say } }
print_story_title(|@a, $wv)
},
);
}
$a.activate.tap({
my $wv = WebkitGTK::WebView.new;
# A lot more work when integrating with JavaScript.
$wv.load-changed.tap(-> *@a {
get_story_title($wv) if @a[1] == WEBKIT_LOAD_FINISHED;
});
$wv.load_uri("http://slashdot.org/");
$a.window.add($wv);
$a.window.title = 'Perl6 Browser';
$a.window.show_all;
});
$a.run;
Here is the latest code for the simple UI version:
use v6.c;
use GTK::Application;
use GTK::Builder;
use WebkitGTK::Raw::Types;
use WebkitGTK::HitTestResult;
use WebkitGTK::WebView;
my $a = GTK::Application.new( title => 'org.genex.p6-browser' );
my $b = GTK::Builder.new( pod => $=pod );
my $wv = WebkitGTK::WebView.new;
my (%cids, $tc, $last_s);
sub update_statusbar($s, $wv, $htr, $m, $ud) {
my $hit = WebkitGTK::HitTestResult.new($htr);
if $hit.context_is_link {
with $tc { $tc.cancel; $tc = Nil; }
my $k = $hit.get_link_uri;
$k = $k.substr(0, 63) ~ '…';
%cids{$k} //= $last_s = $s.get_context_id($k);
$s.push($last_s, $k);
} else {
$s.remove_all($last_s) with $last_s;
$tc = $*SCHEDULER.cue({
$s.remove_all($_) for %cids.values;
%cids = (); $tc = Nil;
}, in => 0.2);
}
}
sub load {
$wv.load_uri( $b<URIEntry>.text );
}
sub stop {
$wv.stop_loading if $wv.is_loading;
}
sub fwd {
$wv.go_forward if $wv.can_go_forward;
$b<Fwd>.sensitive = $wv.can_go_forward;
$b<Back>.sensitive = $wv.can_go_back;
};
sub back {
$wv.go_back if $wv.can_go_back;
$b<Back>.sensitive = $wv.can_go_back;
$b<Fwd>.sensitive = $wv.can_go_forward;
}
sub handle_loading($s, *@a) {
given @a[1] {
when WEBKIT_LOAD_STARTED {
my $uri = $wv.get_uri;
my $text = "Loading { $uri }……";
$last_s = %cids{$uri} = $s.get_context_id($text);
$s.push($last_s, $text);
}
when WEBKIT_LOAD_REDIRECTED { }
when WEBKIT_LOAD_COMMITTED { }
when WEBKIT_LOAD_FINISHED {
$s.pop($last_s);
$last_s = Nil;
%cids{$_}:delete for %cids.keys;
}
}
}
$a.activate.tap({
my $s = GTK::Statusbar.new;
$b<Back>.sensitive = $b<Fwd>.sensitive = False;
$s.margins = 0;
$wv.mouse-target-changed.tap(-> *@a { update_statusbar($s, |@a) });
$wv.load-changed.tap( -> *@a { handle_loading($s, |@a) });
$b<MainWin>.destroy-signal.tap({ $a.exit });
$b<URIEntry>.activate.tap({ load() });
$b<Back>.clicked.tap( { back() });
$b<Fwd>.clicked.tap( { fwd() });
$b<Stop>.clicked.tap( { stop() });
$b<MainWin>.set_size_request(640, 480);
$b<MainWinBox>.pack_start($wv, True, True);
$b<MainWinBox>.pack_end($s, False);
$b<MainWin>.title = 'Perl6 Browser';
$b<MainWin>.show_all;
});
$a.run;
=begin css
statusbar { font-size: 10px; }
=end css
=begin ui
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="MainWin">
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox" id="MainWinBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">2</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkEntry" id="URIEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">start</property>
<child>
<object class="GtkButton" id="Stop">
<property name="label">gtk-stop</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="image_position">top</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="Back">
<property name="label">gtk-go-back</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="image_position">top</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="Fwd">
<property name="label">gtk-go-forward</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="image_position">top</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="Home">
<property name="label">gtk-home</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="image_position">top</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</interface>
=end ui
Here is the full code if you can see it from the screenshot.