diff --git a/objects/client.c b/objects/client.c
index 3ca5efd58..f929e2bf9 100644
--- a/objects/client.c
+++ b/objects/client.c
@@ -82,6 +82,8 @@
* To get all the clients for a screen use either `screen.clients` or
* `screen.tiled_clients`.
*
+ * @DOC_uml_nav_tables_client_EXAMPLE@
+ *
* @author Julien Danjou <julien@danjou.info>
* @copyright 2008-2009 Julien Danjou
* @coreclassmod client
@@ -129,7 +131,7 @@
* it is only useful when setting or getting these properties require code to
* executed.
*
- * @table awful.object
+ * @table awful.client.object
*/
/** AwesomeWM is about to scan for existing clients.
diff --git a/objects/screen.c b/objects/screen.c
index f4510215c..22df006a4 100644
--- a/objects/screen.c
+++ b/objects/screen.c
@@ -40,6 +40,8 @@
* Furthermore to the classes described here, one can also use signals as
* described in @{signals}.
*
+ * @DOC_uml_nav_tables_screen_EXAMPLE@
+ *
* @author Julien Danjou <julien@danjou.info>
* @copyright 2008-2009 Julien Danjou
* @coreclassmod screen
diff --git a/objects/tag.c b/objects/tag.c
index 401a66a57..3f0dd75b8 100644
--- a/objects/tag.c
+++ b/objects/tag.c
@@ -178,6 +178,8 @@
* the documentation generation, you get the real signal name by
* removing the starting dot.
*
+ * @DOC_uml_nav_tables_tag_EXAMPLE@
+ *
* @author Julien Danjou <julien@danjou.info>
* @copyright 2008-2009 Julien Danjou
* @coreclassmod tag
diff --git a/tests/examples/CMakeLists.txt b/tests/examples/CMakeLists.txt
index c4eef2552..3d2a81ed6 100644
--- a/tests/examples/CMakeLists.txt
+++ b/tests/examples/CMakeLists.txt
@@ -232,15 +232,23 @@ function(run_test test_path namespace escaped_content)
file(READ "${expected_output_path}" expected_output)
endif()
- set(TEST_DOC_CONTENT
- "${TEST_DOC_CONTENT}\n${DOC_LINE_PREFIX}\n${DOC_LINE_PREFIX}**Usage example output**:\n${DOC_LINE_PREFIX}"
- )
+ if (NOT tmp_content MATCHES "--DOC_RAW_OUTPUT")
+ set(TEST_DOC_CONTENT
+ "${TEST_DOC_CONTENT}\n${DOC_LINE_PREFIX}\n${DOC_LINE_PREFIX}**Usage example output**:\n${DOC_LINE_PREFIX}"
+ )
+
+ # Markdown requires an empty line before and after, and 4 spaces.
+ escape_string(
+ "\n${expected_output}"
+ "${TEST_DOC_CONTENT}" TEST_DOC_CONTENT " "
+ )
+ else()
+ escape_string(
+ "\n${expected_output}"
+ "${TEST_DOC_CONTENT}" TEST_DOC_CONTENT ""
+ )
+ endif()
- # Markdown requires an empty line before and after, and 4 spaces.
- escape_string(
- "\n${expected_output}"
- "${TEST_DOC_CONTENT}" TEST_DOC_CONTENT " "
- )
set(TEST_DOC_CONTENT "${TEST_DOC_CONTENT}\n${DOC_LINE_PREFIX}")
endif()
@@ -270,14 +278,31 @@ function(run_test test_path namespace escaped_content)
if(NOT ${OUTPUT_IMAGE_PATH} STREQUAL "")
file(RELATIVE_PATH rel_output "${TOP_SOURCE_DIR}" "${OUTPUT_IMAGE_PATH}")
- add_custom_command(
- COMMAND "${TOP_SOURCE_DIR}/tests/examples/runner.sh" "${expected_output_path}" ${LUA_COV_RUNNER} ${template} ${test_path} ${IMAGE_PATH} ${IGNORE_ERRORS}
- # COMMENT "Running ${rel_test_path} (via ${rel_template}, generating ${rel_output})"
- DEPENDS ${template} ${test_path}
- OUTPUT ${OUTPUT_IMAGE_PATH}
- VERBATIM)
+ if(tmp_content MATCHES "--DOC_GEN_OUTPUT")
+ add_custom_command(
+ COMMAND "${TOP_SOURCE_DIR}/tests/examples/runner.sh" "${expected_output_path}" ${LUA_COV_RUNNER} ${template} ${test_path} ${IMAGE_PATH} ${IGNORE_ERRORS}
+ # COMMENT "Running ${rel_test_path} (via ${rel_template}, generating ${rel_output})"
+ DEPENDS ${template} ${test_path}
+ OUTPUT ${OUTPUT_IMAGE_PATH}
+ OUTPUT ${expected_output_path}
+ VERBATIM)
+ else()
+ add_custom_command(
+ COMMAND "${TOP_SOURCE_DIR}/tests/examples/runner.sh" "${expected_output_path}" ${LUA_COV_RUNNER} ${template} ${test_path} ${IMAGE_PATH} ${IGNORE_ERRORS}
+ # COMMENT "Running ${rel_test_path} (via ${rel_template}, generating ${rel_output})"
+ DEPENDS ${template} ${test_path}
+ OUTPUT ${OUTPUT_IMAGE_PATH}
+ VERBATIM)
+ endif()
set(EXAMPLE_DOC_GENERATED_FILES ${OUTPUT_IMAGE_PATH} ${EXAMPLE_DOC_GENERATED_FILES}
PARENT_SCOPE)
+ elseif(tmp_content MATCHES "--DOC_GEN_OUTPUT")
+ add_custom_command(
+ COMMAND "${TOP_SOURCE_DIR}/tests/examples/runner.sh" "${expected_output_path}" ${LUA_COV_RUNNER} ${template} ${test_path} "" ${IGNORE_ERRORS}
+ # COMMENT "Running ${rel_test_path} (via ${rel_template}, generating ${rel_output})"
+ DEPENDS ${template} ${test_path}
+ OUTPUT ${expected_output_path}
+ VERBATIM)
endif()
# Export the outout to the parent scope.
diff --git a/tests/examples/runner.sh b/tests/examples/runner.sh
index 2c1e7022e..3084e934b 100755
--- a/tests/examples/runner.sh
+++ b/tests/examples/runner.sh
@@ -49,6 +49,7 @@ if ! cmp --silent "${file_stdout}" "${expected_output}"
then
echo "Expected text from ${expected_output}, but got:"
diff -u "${expected_output}" "${file_stdout}" || true
+ cp "${file_stdout}" "${expected_output}"
exit 1
fi
diff --git a/tests/examples/uml/nav_tables/client.lua b/tests/examples/uml/nav_tables/client.lua
new file mode 100644
index 000000000..e46a04d94
--- /dev/null
+++ b/tests/examples/uml/nav_tables/client.lua
@@ -0,0 +1,101 @@
+--DOC_GEN_OUTPUT --DOC_NO_USAGE --DOC_HIDE_ALL --DOC_ASTERISK --DOC_RAW_OUTPUT --DOC_GEN_IMAGE
+local module = ...
+
+module.generate_nav_table {
+ class = "client",
+ content = {
+ {
+ association = "aggregation",
+ class = "tag",
+ to_property = "c.tags",
+ from_property = "t:clients()",
+ left = {
+ msg = "Is tagged on",
+ card = "1..N",
+ },
+ right = {
+ msg = "Attached to",
+ card = "0..N",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "screen",
+ to_property = "c.screen",
+ from_property = "s.clients",
+ left = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ right = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "screen",
+ from_property = "s.hidden_clients",
+ left = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ right = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "screen",
+ from_property = "s.tiled_clients",
+ left = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ right = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "awful.key",
+ to_property = "c:keys()",
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ right = {
+ msg = "Attached to",
+ card = "0..N",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "awful.button",
+ to_property = "c:buttons()",
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ right = {
+ msg = "Attached to",
+ card = "0..N",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "mouse",
+ from_property = "mouse.current_client",
+ left = {
+ msg = "Is below",
+ card = "0..1",
+ },
+ right = {
+ msg = "Is over",
+ card = "0..1",
+ },
+ },
+ }
+}
diff --git a/tests/examples/uml/nav_tables/client.output.txt b/tests/examples/uml/nav_tables/client.output.txt
new file mode 100644
index 000000000..65bc1681b
--- /dev/null
+++ b/tests/examples/uml/nav_tables/client.output.txt
@@ -0,0 +1,77 @@
+
+
+Core components relationship
+===
+
+
+ |
+ |
+
+
+ Legend: c: a client object, t: a tag object,
+ s: a screen object, k: an awful.key object,
+ b: a awful.button object, n: a naughty.notification object
+ |
+
+
diff --git a/tests/examples/uml/nav_tables/screen.lua b/tests/examples/uml/nav_tables/screen.lua
new file mode 100644
index 000000000..4e80253f6
--- /dev/null
+++ b/tests/examples/uml/nav_tables/screen.lua
@@ -0,0 +1,101 @@
+--DOC_GEN_OUTPUT --DOC_NO_USAGE --DOC_HIDE_ALL --DOC_ASTERISK --DOC_RAW_OUTPUT --DOC_GEN_IMAGE
+local module = ...
+
+module.generate_nav_table {
+ class = "screen",
+ content = {
+ {
+ association = "aggregation",
+ class = "tag",
+ to_property = "s.tags",
+ from_property = "t.screen",
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ right = {
+ msg = "Attached to",
+ card = "1..1",
+ },
+ },
+ {
+ association = "composition",
+ class = "tag",
+ to_property = "s.selected_tag",
+ left = {
+ msg = "Has",
+ card = "0..1",
+ },
+ right = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ },
+ {
+ association = "composition",
+ class = "tag",
+ to_property = "s.selected_tags",
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ right = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "client",
+ from_property = "c.screen",
+ to_property = "s.clients",
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ right = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "client",
+ to_property = "s.hidden_clients",
+ left = {
+ msg = "Has",
+ card = "1..1",
+ },
+ right = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "client",
+ to_property = "s.tiled_clients",
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ right = {
+ msg = "Has",
+ card = "1..1",
+ },
+ },
+ {
+ association = "aggregation",
+ class = "naughty.notification",
+ from_property = "n.screen",
+ left = {
+ msg = "Displays",
+ card = "0..N",
+ },
+ right = {
+ msg = "Is displayed on",
+ card = "1..N",
+ },
+ },
+ }
+}
diff --git a/tests/examples/uml/nav_tables/screen.output.txt b/tests/examples/uml/nav_tables/screen.output.txt
new file mode 100644
index 000000000..d7b307854
--- /dev/null
+++ b/tests/examples/uml/nav_tables/screen.output.txt
@@ -0,0 +1,77 @@
+
+
+Core components relationship
+===
+
+
+ |
+ |
+
+
+ Legend: c: a client object, t: a tag object,
+ s: a screen object, k: an awful.key object,
+ b: a awful.button object, n: a naughty.notification object
+ |
+
+
diff --git a/tests/examples/uml/nav_tables/tag.lua b/tests/examples/uml/nav_tables/tag.lua
new file mode 100644
index 000000000..6637c6171
--- /dev/null
+++ b/tests/examples/uml/nav_tables/tag.lua
@@ -0,0 +1,62 @@
+--DOC_GEN_OUTPUT --DOC_NO_USAGE --DOC_HIDE_ALL --DOC_ASTERISK --DOC_RAW_OUTPUT --DOC_GEN_IMAGE
+local module = ...
+
+module.generate_nav_table {
+ class = "tag",
+ content = {
+ {
+ association = "aggregation",
+ class = "client",
+ from_property = "c.tags",
+ to_property = "t:clients()",
+ right = {
+ msg = "Is tagged on",
+ card = "1..N",
+ },
+ left = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ {
+ association = "composition",
+ class = "screen",
+ to_property = "t.screen",
+ from_property = "s.tags",
+ left = {
+ msg = "Is displayed on",
+ card = "1..1",
+ },
+ right = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ {
+ association = "composition",
+ class = "screen",
+ from_property = "s.selected_tag",
+ left = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ right = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ {
+ association = "composition",
+ class = "screen",
+ from_property = "s.selected_tags",
+ left = {
+ msg = "Is on",
+ card = "1..1",
+ },
+ right = {
+ msg = "Has",
+ card = "0..N",
+ },
+ },
+ }
+}
diff --git a/tests/examples/uml/nav_tables/tag.output.txt b/tests/examples/uml/nav_tables/tag.output.txt
new file mode 100644
index 000000000..55b9ac223
--- /dev/null
+++ b/tests/examples/uml/nav_tables/tag.output.txt
@@ -0,0 +1,62 @@
+
+
+Core components relationship
+===
+
+
+ |
+ |
+
+
+ Legend: c: a client object, t: a tag object,
+ s: a screen object, k: an awful.key object,
+ b: a awful.button object, n: a naughty.notification object
+ |
+
+
diff --git a/tests/examples/uml/template.lua b/tests/examples/uml/template.lua
new file mode 100644
index 000000000..1b24ede41
--- /dev/null
+++ b/tests/examples/uml/template.lua
@@ -0,0 +1,228 @@
+local file_path, image_path = ...
+require("_common_template")(...)
+local cairo = require("lgi").cairo
+local shape = require("gears.shape")
+local wibox = require("wibox")
+local beautiful = require("beautiful")
+
+-- Make the path relative.
+image_path = "../" .. image_path:match("/(images/[^/]+)$")
+
+-- This template generates an HTML table with how other classes are associated
+-- with a given class.
+
+local function gen_class(name)
+ return wibox.widget {
+ {
+ {
+ {
+ valign = "center",
+ align = "center",
+ markup = ""..name.."",
+ widget = wibox.widget.textbox
+ },
+ fg = "#ffffff",
+ bg = beautiful.bg_normal,
+ widget = wibox.container.background,
+ },
+ nil,
+ nil,
+ widget = wibox.layout.align.vertical,
+ },
+ shape = function(cr,w,h)
+ shape.partially_rounded_rect(cr,w,h, true,true,false,false,5)
+ end,
+ forced_width = 75,
+ border_width = 1,
+ border_strategy = "inner",
+ border_color = beautiful.border_color,
+ bg = "#00000000",
+ widget = wibox.container.background
+ }
+end
+
+local function gen_arrow(association)
+ local w = wibox.widget.base.make_widget()
+
+ function w:fit(_, width, _)
+ return width, 15
+ end
+
+ function w:draw(_, cr, width, height)
+ cr:set_line_width(1.5)
+ cr:move_to(0, height/2)
+ cr:line_to(width-(association == "association" and 0 or 40), height/2)
+ cr:stroke()
+
+ if association ~= "association" then
+ cr:set_antialias(cairo.ANTIALIAS_SUBPIXEL)
+ cr:set_line_width(1)
+ cr:translate(0,2)
+ width, height = width-2, height-4
+ cr:move_to(width-40, height/2)
+ cr:line_to(width-20, 0 )
+ cr:line_to(width , height/2)
+ cr:line_to(width-20, height )
+ cr:line_to(width-40, height/2)
+ cr:close_path()
+
+ if association == "composition" then
+ cr:fill()
+ else
+ cr:stroke()
+ end
+ end
+ end
+
+ w.forced_height = 15
+
+ return w
+end
+
+local function arrow_widget(dir)
+ local w = wibox.widget.base.make_widget()
+
+ function w:fit(_, width, _)
+ return width, 5
+ end
+
+ function w:draw(_, cr, width, height)
+
+ cr:translate(10, 0)
+ width = width - 20
+
+ cr:set_source_rgba(0,0,0,0.2)
+ cr:set_line_width(1)
+ cr:move_to(0, height/2)
+ cr:line_to(width,height/2)
+ cr:stroke()
+
+ if dir == "left" then
+ cr:move_to(height/2, 0)
+ cr:line_to(0, height/2)
+ cr:line_to(height/2, height)
+ cr:stroke()
+ else
+ cr:move_to(width-height/2, 0)
+ cr:line_to(width, height/2)
+ cr:line_to(width-height/2, height)
+ cr:stroke()
+ end
+ end
+
+ return w
+end
+
+local function gen_table_uml(entry, class1, class2, revert)
+ local left = revert and entry.right or entry.left
+ local right = revert and entry.left or entry.right
+
+ return wibox.widget {
+ gen_class(class2),
+ {
+ {
+ {
+ text = " "..left.card,
+ widget = wibox.widget.textbox
+ },
+ arrow_widget "left",
+ {
+ wrap = "char",
+ markup = left.msg.." .",
+ widget = wibox.widget.textbox
+ },
+ spacing = 5,
+ layout = wibox.layout.align.horizontal
+ },
+ gen_arrow(entry.composition),
+ {
+ {
+ wrap = "char",
+ markup = right.msg.." .",
+ widget = wibox.widget.textbox
+ },
+ arrow_widget "right",
+ {
+ text = right.card.." ",
+ widget = wibox.widget.textbox
+ },
+ spacing = 5,
+ layout = wibox.layout.align.horizontal
+ },
+ layout = wibox.layout.fixed.vertical,
+ },
+ gen_class(class1),
+ layout = wibox.layout.align.horizontal
+ }
+end
+
+local map = {
+ to = function(o) return "Acquire other objects from a "..o end,
+ from = function(o) return "Acquire a "..o.." from other objects" end
+}
+
+local function gen_table_header(title, o)
+ print([[ | '
+end
+
+local module = {}
+
+local counter = 1
+
+function module.generate_nav_table(t)
+ assert(t.content and t.class)
+
+ print("\n\nCore components relationship\n===\n")
+ print ''
+
+ -- Validate early to avoid debugging cryptic backtraces.
+ for _, tab in ipairs {"to", "from"} do
+ gen_table_header(tab, t.class)
+ for _, entry in ipairs(t.content) do
+ if entry[tab.."_property"] then
+ assert(entry.left and entry.right and entry.association)
+ assert(entry.class)
+ assert(entry.left.msg and entry.left.card)
+ assert(entry.right.msg and entry.right.card)
+ local path = image_path..counter..".svg"
+ local widget = gen_table_uml(entry, t.class, entry.class, false)
+ wibox.widget.draw_to_svg_file(widget, path, 320, 50)
+ get_table_row(path, entry.class, entry[tab.."_property"])
+ counter = counter + 1
+ end
+ end
+ get_table_footer()
+ end
+ print([[
+
+ Legend: c: a client object, t: a tag object,
+ s: a screen object, k: an awful.key object,
+ b: a awful.button object, n: a naughty.notification object
+ |
+
+
]])
+end
+
+loadfile(file_path)(module)
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80