[gnome-devel-docs] [demos/message-board] More message board content, source code
- From: Shaun McCance <shaunm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-devel-docs] [demos/message-board] More message board content, source code
- Date: Tue, 7 Dec 2010 16:07:15 +0000 (UTC)
commit 21718b80e2daa379593cecdbd26b83b5e29ebb75
Author: Shaun McCance <shaunm gnome org>
Date: Mon Dec 6 18:27:32 2010 -0500
[demos/message-board] More message board content, source code
demos/C/media/message-board.ogv | Bin 0 -> 347904 bytes
demos/C/message-board.c.page | 316 +++++++++++++++++++++++++++++----
demos/C/message-board/message-board.c | 144 +++++++++++++++
3 files changed, 423 insertions(+), 37 deletions(-)
---
diff --git a/demos/C/media/message-board.ogv b/demos/C/media/message-board.ogv
new file mode 100644
index 0000000..c78a279
Binary files /dev/null and b/demos/C/media/message-board.ogv differ
diff --git a/demos/C/message-board.c.page b/demos/C/message-board.c.page
index 7168f3b..84b6107 100644
--- a/demos/C/message-board.c.page
+++ b/demos/C/message-board.c.page
@@ -6,12 +6,13 @@
<link type="guide" xref="index#c"/>
- <desc>XXX</desc>
+ <desc>A simple program using WebKitGTK+ and the DOM.</desc>
- <revision pkgversion="0.1" version="0.1" date="2010-12-02" status="stub"/>
- <credit type="author">
- <name>GNOME Documentation Project</name>
- <email>gnome-doc-list gnome org</email>
+ <revision pkgversion="0.1" version="0.1" date="2010-12-06" status="draft"/>
+ <credit type="author copyright">
+ <name>Shaun McCance</name>
+ <email>shaunm gnome org</email>
+ <years>2010</years>
</credit>
</info>
@@ -21,23 +22,36 @@
<synopsis>
<p>In this tutorial, you will learn:</p>
<list style="compact">
- <item><p>how to display a web page with WebKit</p></item>
- <item><p>YYY</p></item>
- <item><p>ZZZ</p></item>
+ <item><p>How to display a web page with WebKit.</p></item>
+ <item><p>How to manipulate the contents of a web page using WebKit's DOM
+ functions.</p></item>
</list>
+ <p>This tutorial assumes you are familiar with the C programming language
+ and have a basic understanding of GTK+, including how to create and place
+ widgets and how to connect callback functions to signals. See <link
+ xref="image-viewer.c"/> to learn the basics of GTK+.</p>
</synopsis>
-<media type="image" mime="image/png" src="media/message-board.png"/>
+<media type="video" mime="video/ogg" src="media/message-board.ogv"/>
<e:links type="section" xmlns:e="http://projectmallard.org/experimental/"/>
-<section id="setup">
- <title>Some initial set-up stuff</title>
+<section id="create">
+ <title>Create a project in Anjuta</title>
+
+ <p>The GNOME platform includes WebKitGTK+, built on top of the powerful
+ WebKit HTML framework. WebKit is used throughout GNOME, not just to view
+ web pages on the Internet, but also to create rich user interfaces that
+ can be easily styled with CSS.</p>
+
+ <p>In this tutorial, you will create a simple message board using WebKit.
+ The message board will allow you to enter some text and have it added to a
+ list of messages in HTML. Before you begin, you need to set up a project in
+ Anjuta.</p>
<steps>
- <item><p>Start Anjuta.</p></item>
- <item><p>Click <guiseq><gui>File</gui><gui>New</gui><gui>Project</gui></guiseq>
- to open the new project assistant.</p></item>
+ <item><p>In Anjuta, click <guiseq><gui>File</gui><gui>New</gui>
+ <gui>Project</gui></guiseq> to open the new project assistant.</p></item>
<item><p>Select <gui>GTK+ (Simple)</gui> on the <gui>C</gui> tab,
and click <gui>Forward</gui>.</p></item>
<item><p>Fill out your details on the <gui>Basic information</gui> page.
@@ -47,13 +61,35 @@
On the <gui>Project options</gui> page, select <gui>Configure external
packages</gui>. Click <gui>Forward</gui>. On the <gui>Configure external
packages</gui> page, check <gui>webkitgtk-3.0</gui>.</p></item>
- <item><p>After you finish the new project assistant, ...</p></item>
- <item><p>Open <file>main.c</file>.</p></item>
</steps>
- <p>After <code>#include <gtk/gtk.h></code>, add line
- <code>#include <webkit/webkit.h></code>. Replace the
- <code>create_window</code> function with the following:</p>
+ <p>After you finish the new project assistant, open the file
+ <file>src/main.c</file> from either the <gui>Project</gui> or the
+ <gui>File</gui> tab. Anjuta will have filled this in with some basic
+ GTK+ code from its templates. Since you are creating a WebKit project,
+ you first need to include the WebKit headers. After the line that
+ includes <code>gtk/gtk.h</code>, add the following line:</p>
+
+ <code>#include <webkit/webkit.h></code>
+
+ <p>Verify that everything works by building what you have so far.
+ Click <guiseq><gui>Build</gui><gui>Build Project</gui></guiseq> or
+ just press <keyseq><key>Shift</key><key>F7</key></keyseq>. The first
+ time you build, you will be asked for some configure options. Just
+ accept the defaults and click <gui>Execute</gui>.</p>
+
+ <p>You should now be able to run the program. Click <guiseq>
+ <gui>Run</gui><gui>Execute</gui></guiseq> or just press <key>F3</key>.
+ You should see an empty window appear.</p>
+</section>
+
+<section id="webview">
+ <title>Lay out your window and web view</title>
+
+ <p>Now that you can show a window, it's time to start working with WebKit.
+ For this tutorial, you'll create a text entry and a web view and pack
+ them both into a window. Find the function <code>create_window</code> and
+ replace it with the following:</p>
<code style="numbered" mime="text/C"><![CDATA[
static GtkWidget*
@@ -63,8 +99,11 @@ create_window (void)
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
+ gtk_window_set_title (GTK_WINDOW (window), "Message Board");
+ g_signal_connect (window, "delete-event", G_CALLBACK (destroy), NULL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 6);
gtk_container_add (GTK_CONTAINER (window), box);
entry = gtk_entry_new ();
@@ -86,11 +125,103 @@ create_window (void)
return window;
}
]]></code>
+
+ <p>You first create a <code>GtkWindow</code> object and set its title and
+ default size. You also connect the <code>destroy</code> function to the
+ <code>delete-event</code> signal. The <code>delete-event</code> signal is
+ emitted when the window is closed. The <code>destroy</code> function is
+ already provided in <file>main.c</file>. It just calls
+ <code>gtk_main_quit</code> to quit the application.</p>
+
+ <p>You then create a vertical box and add it to the window. A window can only
+ hold a single child widget, so you need to use a box to add multiple widgets.
+ The second argument to <code>gtk_box_new</code> sets the amount of padding
+ (in pixels) between each child, and the next line puts a six-pixel border
+ around the entire thing.</p>
+
+ <p>You next create a <code>GtkEntry</code> object and pack it into the box.
+ The third and fourth arguments to <code>gtk_box_pack_start</code> specify that
+ the entry shouldn't take up any extra space the box has available. The fourth
+ argument is the amount of padding you want around the entry. In this case,
+ you set the padding to zero, because you're allowing the box to handle all
+ the padding.</p>
+
+ <p>Before you add a web view, you have to create a scrolled window to put it
+ inside of. The scrolled window will place scrollbars on the right and bottom
+ when necessary, and prevent your web view from filling your entire screen.
+ This time, you pass <code>TRUE</code> and <code>TRUE</code> to
+ <code>gtk_box_pack_start</code> to allow the scrolled window (and thus, the
+ web view) to use any extra space available in the box.</p>
+
+ <p>Finally, you create a <code>WebKitWebView</code> and add it to the scrolled
+ window. Then load a very basic HTML page into the web view by calling
+ <code>webkit_web_view_load_string</code> with the follow arguments:</p>
+
+ <terms>
+ <item>
+ <title><code>WEBKIT_WEB_VIEW (view)</code></title>
+ <p>The view itself. Because <code>view</code> is typed as a
+ <code>GtkWidget*</code>, you have to use <code>WEBKIT_WEB_VIEW</code>
+ to safely cast the object.</p>
+ </item>
+ <item>
+ <title><code>"<html><body></body></html>"</code></title>
+ <p>The simplest HTML file you could possibly write.</p>
+ </item>
+ <item>
+ <title><code>"text/html"</code></title>
+ <p>The MIME type of the content you provided. In this case, you're
+ using plain HTML.</p>
+ </item>
+ <item>
+ <title><code>"UTF-8"</code></title>
+ <p>The character encoding of the content you provided. Although you only
+ used ASCII characters, it's a good idea to specify UTF-8. UTF-8 is used
+ as the default encoding throughout the GNOME platform.</p>
+ </item>
+ <item>
+ <title><code>NULL</code></title>
+ <p>The base URI. You don't need it in this simple example, but you might
+ want to provide a <sys>file:</sys> URI if you add images or other features
+ where you want to use relative URI references.</p>
+ </item>
+ </terms>
+
+ <note style="sidebar">
+ <p>Every time you add a widget, you have to call <code>gtk_widget_show</code>
+ on it for it to be visible. If you call <code>gtk_widget_show_all</code> on
+ a container widget like a <code>GtkBox</code>, GTK+ will automatically show
+ all the widgets inside the container, to any depth. Sometimes you don't
+ want to call <code>gtk_widget_show_all</code>, such as when you want to
+ dynamically hide and show some widgets in response to events.</p>
+ </note>
+
+ <p>Finally, you have to call <code>gtk_widget_show_all</code> on the box.
+ Otherwise, none of the widgets you created will be visible. (The window is
+ shown in the <code>main</code> function with <code>gtk_widget_show</code>.)</p>
+
+ <p>Build and run the message board again. You should see a window with a text
+ entry and a web view. It doesn't do anything yet because the text entry and
+ the web view don't know anything about each other.</p>
</section>
<section id="signals">
<title>Hook up signals</title>
- <p></p>
+
+ <p>Now you want to make the message board actually <em>do</em> something
+ when you enter text into the text entry. To do this, connect a callback
+ function to the <code>activate</code> signal of <code>entry</code>. GTK+
+ emits the <code>activate</code> signal whenever the user presses
+ <key>Enter</key> in the entry. Add the following into <code>create_window</code>,
+ anywhere after both <code>entry</code> and <code>view</code> have been defined:</p>
+
+<code><![CDATA[
+g_signal_connect (entry, "activate", G_CALLBACK (entry_activate_cb), view);
+]]></code>
+
+ <p>You then have to actually define <code>entry_activate_cb</code>. Define
+ it as follows, anywhere above <code>create_window</code>:</p>
+
<code style="numbered"><![CDATA[
static void
entry_activate_cb (GtkEntry *entry, WebKitWebView *view)
@@ -99,12 +230,8 @@ entry_activate_cb (GtkEntry *entry, WebKitWebView *view)
WebKitDOMElement *body, *div;
document = webkit_web_view_get_dom_document (view);
- body = webkit_dom_document_query_selector (document,
- "body",
- NULL);
- div = webkit_dom_document_create_element (document,
- "div",
- NULL);
+ body = webkit_dom_document_query_selector (document, "body", NULL);
+ div = webkit_dom_document_create_element (document, "div", NULL);
webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (div),
gtk_entry_get_text (entry),
NULL);
@@ -115,20 +242,63 @@ entry_activate_cb (GtkEntry *entry, WebKitWebView *view)
}
]]></code>
-<p>Into <code>create_window</code> function, after both
-<code>entry</code> and <code>view</code> have been defined:</p>
+ <p>The first thing you do is get a <code>WebKitDOMDocument</code> object
+ that represents the HTML document displayed in <code>view</code>. The DOM
+ classes and methods in WebKit allow you to inspect and manipulate the HTML
+ document, and work very similarly to the DOM APIs you might already know
+ from JavaScript.</p>
-<code><![CDATA[
-g_signal_connect (entry, "activate",
- G_CALLBACK (entry_activate_cb), view);
-]]></code>
+ <p>Once you have the document, you want to get the <code>body</code> element
+ so that you can add <code>div</code> elements to it. The
+ <code>webkit_dom_document_query_selector</code> function lets you find an
+ element in the document using CSS selectors. This keeps you from having to
+ write tedious loops to traverse the document.</p>
+
+ <comment>
+ <cite>shaunm</cite>
+ <p>FIXME: Is this true? Does query_selector take CSS, CSSish, or what?</p>
+ </comment>
+
+ <p>Next, you create a new <code>div</code> element to hold the message. Every
+ element you create has to be attached to a document, so the function to create
+ an element takes the <code>WebKitDOMDocument</code> as its first arguments.
+ You then set the text content of the element to the contents of the text entry.
+ Because <code>gtk_entry_get_text</code> returns a <code>const gchar*</code>,
+ you don't have to free the result.</p>
+
+ <comment>
+ <cite>shaunm</cite>
+ <p>Not passing the GError**, but we should give it a quick mention and
+ link to somewhere that explains how GError-handling works.</p>
+ </comment>
+
+ <p>Finally, you append the new <code>div</code> element to the body and
+ clear out the text entry so you can type something new. Build and run the
+ program again and test it for yourself.</p>
</section>
+
<section id="css">
- <title>CSS</title>
-<p>Anywhere above <code>create_window</code>:</p>
+ <title>Make it look better with CSS</title>
+
+ <p>At this point, your program is completely functional, but not very pretty.
+ You can style the message display with CSS, just like you can with any other
+ HTML page. There are many ways you could attach some CSS to the page: You
+ could add it in the initial HTML document. You could inline it in the
+ <code>style</code> attribute of the <code>div</code> elements. You could
+ even programmatically construct it using the DOM APIs.</p>
+
+ <p>In this tutorial, you'll attach the CSS using the <code>user-stylesheet-uri</code>
+ property of the <code>WebKitWebSetting</code> object attached to your web view.
+ In a more complete application, you would want to save and load your HTML file.
+ Keeping the style information outside the actual HTML means that you can change
+ the styling completely within your application, without having to change users'
+ files. You would normally just install a file along with your application, but
+ just to keep everything in one file for this demo, we'll use a trick called a
+ data URI. First, define the CSS as a static string near the top of your file.</p>
+
<code><![CDATA[
-static const char CSS[] =
+static const guchar CSS[] =
"body { margin: 0; padding: 0; }\n"
"div { "
" -webkit-border-radius: 2px;"
@@ -140,10 +310,19 @@ static const char CSS[] =
"}";
]]></code>
+ <p>All you have in this example are <code>div</code> elements inside a
+ <code>body</code> element. If you created more complicated HTML, you could
+ use whatever CSS is necessary. In fact, if you're comfortable with CSS, you
+ should trying changing this to something you like better.</p>
+
+ <p>To apply the CSS, you set the <code>user-stylesheet-uri</code> in the
+ <code>create_window</code> function, anywhere after <code>view</code> has
+ already been defined.</p>
+
+ <comment><cite>shaunm</cite><p>g_base64_encode has bad args</p></comment>
-<p>In <code>create_window</code>:</p>
<code><![CDATA[
-tmp = g_base64_encode (CSS, strlen(CSS));
+tmp = g_base64_encode (CSS, strlen((gchar *) CSS));
css = g_strconcat ("data:text/css;charset=utf-8;base64,",
tmp, NULL);
g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)),
@@ -152,6 +331,69 @@ g_free (css);
g_free (tmp);
]]></code>
+ <p>Also, make sure to add variable declarations for <code>tmp</code>
+ and <code>css</code> to the top of <code>create_window</code>.</p>
+<code>
+gchar *tmp, *css;
+</code>
+
+ <p>A data URI starts with <sys>data:</sys> and some information about
+ the content type and how the data is encoded. The actual data follows after
+ a comma, in this case encoded in Base64. Unlike other URI schemes like
+ <sys>http:</sys>, <sys>ftp:</sys>, and <sys>file:</sys>, the <sys>data:</sys>
+ URI scheme doesn't specify where to find a file to load. Rather, it gives
+ the entire contents of the file.</p>
+
+ <p>The code above first encodes your CSS definitions in Base64, then
+ combines that with a fixed string to create a data URI. The
+ <code>g_strconcat</code> function can take any number of string arguments
+ and concatenate them all together, so you have to pass <code>NULL</code>
+ as the final argument so it knows when to stop. And don't forget to free
+ those temporary strings after you set the stylesheet property.</p>
+
+ <p>Build and run the program again. It should now work exactly the same
+ as at the end of the last section, except the messages will be nicely
+ styled with a border and a subtle background gradient.</p>
+</section>
+
+<section id="more">
+ <title>Learn more</title>
+
+ <p>This tutorial showed you how to create a basic application using GTK+
+ and WebKit, including showing a document and manipulating its contents.
+ To create a real application, you probably want to do a little bit more.
+ Try adding features on your own. Here are a few ideas:</p>
+
+ <list>
+ <item><p>If you're comfortable with CSS, try changing the style of the
+ message display. CSS is easy to get started with, but increasingly more
+ powerful. There is a wealth of CSS tutorials on the Internet, and just
+ about everything you can do on the web, you can do in this
+ application.</p></item>
+
+ <item><p>Right now, you lose all your messages whenever you close the
+ message board. Try saving the HTML contents after each post, and loading
+ the saved file (if it exists) on startup.</p>
+ <comment>
+ <cite>shaunm</cite><p>Link to method to get HTML from DOM and to
+ GIO APIs.</p>
+ </comment></item>
+
+ <item><p>If you keep your messages around for a long time, you'll start
+ wondering when you posted them. Add a timestamp to each message when it's
+ posted. You'll probably want to create some additional child <code>div</code>
+ elements with different classes that you can style in the CSS.</p>
+ <comment><cite>shaunm</cite><p>Link to strftime or something</p></comment>
+ </item>
+
+ <item><p>This program keeps messages around forever. Think about ways
+ you could allow the user to delete messages. Perhaps you want messages
+ to disappear automatically after they're too old, or after there are a
+ certain number of messages before them. Or you could add a link in each
+ message to delete it. You could even override the context menu when you
+ right-click on a message. These features involve exploring WebKit's DOM
+ API more.</p></item>
+ </list>
</section>
</page>
diff --git a/demos/C/message-board/message-board.c b/demos/C/message-board/message-board.c
new file mode 100644
index 0000000..536d1aa
--- /dev/null
+++ b/demos/C/message-board/message-board.c
@@ -0,0 +1,144 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * main.c
+ * Copyright (C) Shaun McCance 2010 <shaunm gnome org>
+ *
+ * message-board is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * message-board 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+
+#include <glib/gi18n.h>
+
+
+/* For testing propose use the local (not installed) ui file */
+/* #define UI_FILE PACKAGE_DATA_DIR"/message_board/ui/message_board.ui" */
+#define UI_FILE "src/message_board.ui"
+
+/* Signal handlers */
+/* Note: These may not be declared static because signal autoconnection
+ * only works with non-static methods
+ */
+
+static const guchar CSS[] =
+"body { margin: 0; padding: 0; }\n"
+"div { "
+" -webkit-border-radius: 2px;"
+" background: -webkit-gradient(linear, 0% 100%, 0% 0%,"
+" from(#f1f1f1), to(white));"
+" border: solid 1px #c6c6c6;"
+" -webkit-box-shadow: 0px 0px 2px #c6c6c6;"
+" margin: 12px; padding: 6px;"
+"}";
+
+/* Called when the window is closed */
+void
+destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+static void
+entry_activate_cb (GtkEntry *entry, WebKitWebView *view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMElement *body, *div;
+
+ document = webkit_web_view_get_dom_document (view);
+ body = webkit_dom_document_query_selector (document,
+ "body",
+ NULL);
+ div = webkit_dom_document_create_element (document,
+ "div",
+ NULL);
+ webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (div),
+ gtk_entry_get_text (entry),
+ NULL);
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (body),
+ WEBKIT_DOM_NODE (div),
+ NULL);
+ gtk_entry_set_text (entry, "");
+}
+
+static GtkWidget*
+create_window (void)
+{
+ GtkWidget *window, *box, *scroll, *view, *entry;
+ gchar *tmp, *css;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
+ gtk_window_set_title (GTK_WINDOW (window), "Message Board");
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (destroy), NULL);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 6);
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0);
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ g_object_set (scroll, "shadow-type", GTK_SHADOW_IN, NULL);
+ gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
+
+ view = webkit_web_view_new ();
+ gtk_container_add (GTK_CONTAINER (scroll), view);
+ webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
+ "<html><body></body></html>",
+ "text/html",
+ "UTF-8",
+ NULL);
+
+ tmp = g_base64_encode (CSS, strlen((gchar *) CSS));
+ css = g_strconcat ("data:text/css;charset=utf-8;base64,",
+ tmp, NULL);
+ g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)),
+ "user-stylesheet-uri", css, NULL);
+ g_free (css);
+ g_free (tmp);
+
+ g_signal_connect (entry, "activate",
+ G_CALLBACK (entry_activate_cb), view);
+
+ gtk_widget_show_all (GTK_WIDGET (box));
+ return window;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+
+ gtk_set_locale ();
+ gtk_init (&argc, &argv);
+
+ window = create_window ();
+ gtk_widget_show (window);
+
+ gtk_main ();
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]