From 1463c3936ce6a57352590b901c9dbd6bc2f2086d Mon Sep 17 00:00:00 2001 From: Sebastian Geerken Date: Mon, 1 Jun 2015 22:00:10 +0200 Subject: Split up user and developer documentation. --- devdoc/CCCwork.txt | 153 +++++++++ devdoc/Cache.txt | 166 +++++++++ devdoc/Dillo.txt | 96 ++++++ devdoc/Dpid.txt | 331 ++++++++++++++++++ devdoc/HtmlParser.txt | 124 +++++++ devdoc/IO.txt | 468 +++++++++++++++++++++++++ devdoc/Images.txt | 129 +++++++ devdoc/NC_design.txt | 127 +++++++ devdoc/README | 51 +++ devdoc/dw-changes.doc | 105 ++++++ devdoc/dw-example-screenshot.png | Bin 0 -> 2264 bytes devdoc/dw-floats-01.png | Bin 0 -> 3410 bytes devdoc/dw-grows.doc | 202 +++++++++++ devdoc/dw-images-and-backgrounds.doc | 235 +++++++++++++ devdoc/dw-layout-views.doc | 256 ++++++++++++++ devdoc/dw-layout-widgets.doc | 267 +++++++++++++++ devdoc/dw-line-breaking.doc | 470 ++++++++++++++++++++++++++ devdoc/dw-map.doc | 59 ++++ devdoc/dw-out-of-flow-2.doc | 69 ++++ devdoc/dw-out-of-flow-floats.doc | 121 +++++++ devdoc/dw-out-of-flow.doc | 214 ++++++++++++ devdoc/dw-overview.doc | 158 +++++++++ devdoc/dw-size-of-widget.png | Bin 0 -> 1749 bytes devdoc/dw-style-box-model.png | Bin 0 -> 3889 bytes devdoc/dw-style-length-absolute.png | Bin 0 -> 575 bytes devdoc/dw-style-length-percentage.png | Bin 0 -> 890 bytes devdoc/dw-style-length-relative.png | Bin 0 -> 868 bytes devdoc/dw-textblock-collapsing-spaces-1-1.png | Bin 0 -> 641 bytes devdoc/dw-textblock-collapsing-spaces-1-2.png | Bin 0 -> 521 bytes devdoc/dw-textblock-collapsing-spaces-2-1.png | Bin 0 -> 802 bytes devdoc/dw-textblock-collapsing-spaces-2-2.png | Bin 0 -> 586 bytes devdoc/dw-usage.doc | 375 ++++++++++++++++++++ devdoc/dw-viewport-with-scrollbar.png | Bin 0 -> 755 bytes devdoc/dw-viewport-without-scrollbar.png | Bin 0 -> 542 bytes devdoc/dw-widget-sizes.doc | 277 +++++++++++++++ devdoc/fltk-problems.doc | 180 ++++++++++ devdoc/index.doc | 48 +++ devdoc/lout.doc | 95 ++++++ devdoc/not-so-simple-container.png | Bin 0 -> 5738 bytes devdoc/rounding-errors.doc | 35 ++ devdoc/uml-legend.doc | 195 +++++++++++ 41 files changed, 5006 insertions(+) create mode 100644 devdoc/CCCwork.txt create mode 100644 devdoc/Cache.txt create mode 100644 devdoc/Dillo.txt create mode 100644 devdoc/Dpid.txt create mode 100644 devdoc/HtmlParser.txt create mode 100644 devdoc/IO.txt create mode 100644 devdoc/Images.txt create mode 100644 devdoc/NC_design.txt create mode 100644 devdoc/README create mode 100644 devdoc/dw-changes.doc create mode 100644 devdoc/dw-example-screenshot.png create mode 100644 devdoc/dw-floats-01.png create mode 100644 devdoc/dw-grows.doc create mode 100644 devdoc/dw-images-and-backgrounds.doc create mode 100644 devdoc/dw-layout-views.doc create mode 100644 devdoc/dw-layout-widgets.doc create mode 100644 devdoc/dw-line-breaking.doc create mode 100644 devdoc/dw-map.doc create mode 100644 devdoc/dw-out-of-flow-2.doc create mode 100644 devdoc/dw-out-of-flow-floats.doc create mode 100644 devdoc/dw-out-of-flow.doc create mode 100644 devdoc/dw-overview.doc create mode 100644 devdoc/dw-size-of-widget.png create mode 100644 devdoc/dw-style-box-model.png create mode 100644 devdoc/dw-style-length-absolute.png create mode 100644 devdoc/dw-style-length-percentage.png create mode 100644 devdoc/dw-style-length-relative.png create mode 100644 devdoc/dw-textblock-collapsing-spaces-1-1.png create mode 100644 devdoc/dw-textblock-collapsing-spaces-1-2.png create mode 100644 devdoc/dw-textblock-collapsing-spaces-2-1.png create mode 100644 devdoc/dw-textblock-collapsing-spaces-2-2.png create mode 100644 devdoc/dw-usage.doc create mode 100644 devdoc/dw-viewport-with-scrollbar.png create mode 100644 devdoc/dw-viewport-without-scrollbar.png create mode 100644 devdoc/dw-widget-sizes.doc create mode 100644 devdoc/fltk-problems.doc create mode 100644 devdoc/index.doc create mode 100644 devdoc/lout.doc create mode 100644 devdoc/not-so-simple-container.png create mode 100644 devdoc/rounding-errors.doc create mode 100644 devdoc/uml-legend.doc (limited to 'devdoc') diff --git a/devdoc/CCCwork.txt b/devdoc/CCCwork.txt new file mode 100644 index 00000000..1ea5d20e --- /dev/null +++ b/devdoc/CCCwork.txt @@ -0,0 +1,153 @@ +Last review: August 04, 2009 --jcid + + +---------------------------- +Internal working for the CCC +---------------------------- + + +HTTP protocol +------------- + + + Query: | + . + 1B --> 1B 1B --> 1B --> | -------------. + .----. .----. .----. . | +I |Capi| |http| | IO | | | + '----' '----' '----' . | + 1F <-- 1F 1F <-- 1F | V + . + | [Server] + Answer: . + + 2B --> 2B 2B --> 2B | | + .----. .----. .----. . | +II |Capi| |Dpi | | IO | | | + '----' '----' '----' . | + 2F <-- 2F 2F <-- 2F <-- | <------------' + . + | + +* a_Capi_open_url() builds both the Answer and Query chains at +once (Answer first then Query), to ensure a uniform structure +that avoids complexity (e.g. race conditions). + +* Http_get() sets a callback for the DNS hostname resolve. +Normally it comes later, but may also by issued immediately if +the hostname is cached. + +* The socket FD is passed by means of OpSend by the http module +once the remote IP is known and the socket is connected. + + + +Function calls for HTTP CCC +--------------------------- + + a_Capi_open_url + if (reload) + Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO] + Capi OpStart 1B (query) [Capi] --> [http] --> [IO] + Http_get + a_Cache_open_url + if URL_E2EReload -> prepare reload + if cached + client enqueue + delayed process queue + else + Cache_entry_add + client enqueue + + -//-> + a_Http_dns_cb + Http_connect_socket + OpSend FD, BCK + OpSend FD, FWD + Http_send_query + a_Http_make_query_str + OpSend, BCK + IO_submit + a_IOwatch_add_fd (DIO_WRITE, ...) + + + Note about 'web' structures. They're created using a_Web_new(). +The web.c module keeps a list of valid webs, so anytime you're +unsure of a weak reference to 'web', it can be checked with +a_Web_valid(web). + + + +------------ +Dpi protocol +------------ + + + Query: | + . + 1B --> 1B 1B --> 1B --> | -------------. + .----. .----. .----. . | +I |Capi| |Dpi | | IO | | | + '----' '----' '----' . | + 1F <-- 1F 1F <-- 1F | V + . + | [Server] + . + Answer (same as HTTP): | | + . | + 2B --> 2B 2B --> 2B | | + .----. .----. .----. . | +II |Capi| |Dpi | | IO | | | + '----' '----' '----' . | + 2F <-- 2F 2F <-- 2F <-- | <------------' + . + | + + +CCC Construction: + + a_Capi_open_url() calls a_Capi_dpi_send_cmd() when the URL +belongs to a dpi and it is not cached. + + a_Capi_dpi_send_cmd() builds both the Answer and Query chains +at once (Answer first then Query), in the same way as HTTP does. +Note that the answer chain is the same for both, and the query +chain only differs in the module in the middle ([http] or [dpi]). + + +Function calls for DPI CCC +-------------------------- + + a_Capi_open_url + a_Capi_dpi_send_cmd + Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO] + Capi OpStart 1B (query) [Capi] --> [http] --> [IO] + a_Cache_open_url + [...] + + +Normal termination: + + When the dpi server is done, it closes the FD, and OpEnd flows +from IO to Capi (answer branch). When in Capi, capi propagates +OpEnd to the query branch. + +Abnormal termination: + + The transfer may be aborted by a_Capi_conn_abort_by_url(). The +OpAbort is not yet standardized and has an ad-hoc implementation. +One idea is to have OpAbort always propagate BCK and then FWD and +to jump into the other chain when it gets to [Capi]. + + +Debugging CCC +------------- + + A simple way to "look" inside it, is to "#define VERBOSE 1" in +chain.c, and then to follow its work with a printed copy of the +diagrams in this document. + + Each new data request generates a CCC, so if you want to debug, +it's good to refine the testcase to the minimum possible number +of connections. + diff --git a/devdoc/Cache.txt b/devdoc/Cache.txt new file mode 100644 index 00000000..4e885df2 --- /dev/null +++ b/devdoc/Cache.txt @@ -0,0 +1,166 @@ + June 2000, --Jcid + Last update: Jul 09 + + ------- + CACHE + ------- + + The cache module is the main abstraction layer between +rendering and networking. + + The capi module acts as a discriminating wrapper which either +calls the cache or the dpi routines depending on the type of +request. + + Every URL must be requested using a_Capi_open_url, which +sends the request to the cache if the data is cached, to dillo's +http module for http: URLs, and through dillo's DPI system for +other URLs. + + Here we'll document non dpi requests. + + + ---------------- + CACHE PHILOSOPHY + ---------------- + + Dillo's cache is very simple; every single resource that's +retrieved (URL) is kept in memory. NOTHING is saved to disk. +This is mainly for three reasons: + + - Dillo encourages personal privacy and it assures there'll be +no recorded tracks of the sites you visited. + + - The Network is full of intermediate transparent proxys that +serve as caches. + + - If you still want to have cached stuff, you can install an +external cache server (such as WWWOFFLE), and benefit from it. + + + --------------- + CACHE STRUCTURE + --------------- + + Currently, dillo's cache code is spread in different sources: +mainly in cache.[ch], dicache.[ch] and it uses some other +functions from mime.c and web.cc. + + Cache.c is the principal source, and it also is the one +responsible for processing cache-clients (held in a queue). +Dicache.c is the interface to the decompressed RGB representations +of currently-displayed images held in DW's imgbuf. + + mime.c and web.cc are used for secondary tasks such as +assigning the right "viewer" or "decoder" for a given URL. + + +---------------- +A bit of history +---------------- + + Some time ago, the cache functions, URL retrieval and +external protocols were a whole mess of mixed code, and it was +getting REALLY hard to fix, improve or extend the functionality. +The main idea of this "layering" is to make code-portions as +independent as possible so they can be understood, fixed, +improved or replaced without affecting the rest of the browser. + + An interesting part of the process is that, as resources are +retrieved, the client (dillo in this case) doesn't know the +Content-Type of the resource at request-time. It only becomes known +when the resource header is retrieved (think of http). This +happens when the cache has control, so the cache sets the +proper viewer for it (unless the Callback function was already +specified with the URL request). + + You'll find a good example in http.c. + + Note: All resources received by the cache have HTTP-style headers. + The file/data/ftp DPIs generate these headers when sending their + non-HTTP resources. Most importantly, a Content-Type header is + generated based on file extension or file contents. + + +------------- +Cache clients +------------- + + Cache clients MUST use a_Capi_open_url to request an URL. The +client structure and the callback-function prototype are defined, +in cache.h, as follows: + +struct _CacheClient { + int Key; /* Primary Key for this client */ + const DilloUrl *Url; /* Pointer to a cache entry Url */ + int Version; /* Dicache version of this Url (0 if not used) */ + void *Buf; /* Pointer to cache-data */ + uint_t BufSize; /* Valid size of cache-data */ + CA_Callback_t Callback; /* Client function */ + void *CbData; /* Client function data */ + void *Web; /* Pointer to the Web structure of our client */ +}; + +typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client); + + + Notes: + + * Op is the operation that the callback is asked to perform + by the cache. { CA_Send | CA_Close | CA_Abort }. + + * Client: The Client structure that originated the request. + + + +-------------------------- +Key-functions descriptions +-------------------------- + +································································ +int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData) + + if Web->url is not cached + Create a cache-entry for that URL + Send client to cache queue + else + Feed our client with cached data + +································································ + +---------------------- +Redirections mechanism + (HTTP 30x answers) +---------------------- + + This is by no means complete. It's a work in progress. + + Whenever an URL is served under an HTTP 30x header, its cache +entry is flagged with 'CA_Redirect'. If it's a 301 answer, the +additional 'CA_ForceRedirect' flag is also set, if it's a 302 +answer, 'CA_TempRedirect' is also set (this happens inside the +Cache_parse_header() function). + + Later on, in Cache_process_queue(), when the entry is flagged +with 'CA_Redirect' Cache_redirect() is called. + + + + + + + +----------- +Notes +----------- + + The whole process is asynchronous and very complex. I'll try +to document it in more detail later (source is commented). + Currently I have a drawing to understand it; hope the ASCII +translation serves the same as the original. + If you're planning to understand the cache process thoroughly, +write me a note and I will assign higher priority to further +improvement of this doc. + Hope this helps! + + diff --git a/devdoc/Dillo.txt b/devdoc/Dillo.txt new file mode 100644 index 00000000..a63c9588 --- /dev/null +++ b/devdoc/Dillo.txt @@ -0,0 +1,96 @@ +"Eliminate the guesswork and quality goes up." + + + ------- + DILLO + ------- + + These notes are written with a view to make it less hard, not +easier yet ;), to get into Dillo development. + When I first got into it, I was totally unaware of the browser +internals. Now that I've made my way deep into the core of it, +(we rewrote it 90% and modified the rest), is time to write some +documentation, just to make a less steep learning curve for new +developers. + + --Jcid + + + + -------- + OVERVIEW + -------- + + Dillo can be viewed as the sum of five main parts: + + 1.- Dillo Widget: A custom widget, FLTK-based, that holds the +necessary data structures and mechanisms for graphical rendering. +(Described in Dw*.txt, dw*.c files among the sources.) + + 2.- Dillo Cache: Integrated with a signal driven Input/Output +engine that handles file descriptor activity, the cache acts as +the main abstraction layer between rendering and networking. + Every URL, whether cached or not, must be retrieved using +a_Capi_open_url (Described briefly in Cache.txt, source +contained in capi.c). + IO is described in IO.txt (recommended), source in src/IO/. + + 3.- The HTML parser: A streamed parser that joins the Dillo +Widget and the Cache functionality to make browsing possible +(Described in HtmlParser.txt, source mainly inside html.cc). + + 4.- Image processing code: The part that handles image +retrieval, decoding, caching and displaying. (Described in +Images.txt. Sources: image.c, dw/image.cc, dicache.c, gif.c, +jpeg.c and png.c) + + 5.- The dpi framework: a gateway to interface the browser with +external programs (Example: the bookmarks server plugin). +Dpi spec: http://www.dillo.org/dpi1.html + + + ------------------------- + HOW IS THE PAGE RENDERED? + ------------------------- + +(A short description of the internal function calling process) + + When the user requests a new URL, a_UIcmd_open_url +is queried to do the job; it calls a_Nav_push (The highest level +URL dispatcher); a_Nav_push updates current browsing history and +calls Nav_open_url. Nav_open_url closes all open connections by +calling a_Bw_stop_clients, and then calls +a_Capi_open_url which calls a_Cache_open_url (or the dpi module if +this gateway is used). + + If Cache_entry_search hits (due to a cached url :), the client is +fed with cached data, but if the URL isn't cached yet, a new CCC +(Concomitant Control Chain) is created and committed to fetch the +URL. + + The next CCC link is dynamically assigned by examining the +URL's protocol. It can be a_Http_ccc or a_Dpi_ccc. + + If we have an HTTP URL, a_Http_ccc will succeed, and the http +module will be linked; it will create the proper HTTP query and +link the IO module to submit and deliver the answer. + + Note that as the Content-Type of the URL is not always known +in advance, the answering branch decides where to dispatch it to +upon HTTP header arrival. + + + What happens then? + + Well, the html parser gets fed, and proper functions are +called for each tag (to parse and call the appropriate methods) +and the whole page is contructed in a streamed way. + Somewhere in the middle of it, resize and repaint functions +are activated and idle functions draw to screen what has been +processed. + + (The process for images is described in Images.txt) + + + + diff --git a/devdoc/Dpid.txt b/devdoc/Dpid.txt new file mode 100644 index 00000000..6c418f57 --- /dev/null +++ b/devdoc/Dpid.txt @@ -0,0 +1,331 @@ +Aug 2003, Jorge Arellano Cid, + Ferdi Franceschini -- +Last update: Nov 2009 + + + ------ + dpid + ------ + +------------- +Nomenclature: +------------- + + dpi: + generic term referring to dillo's plugin system (version1). + + dpi1: + specific term for dillo's plugin spec version 1. + at: http://www.dillo.org/dpi1.html + + dpi program: + any plugin program itself. + + dpi framework: + the code base inside and outside dillo that makes dpi1 + working possible (it doesn't include dpi programs). + + dpip: + dillo plugin protocol. The HTML/XML like set of command tags + and information that goes inside the communication sockets. + Note: not yet fully defined, but functional. + Note2: it was designed to be extensible. + + dpid: + dillo plugin daemon. + + server plugin: + A plugin that is capable of accepting connections on a socket. Dpid will + never run more than one instance of a server plugin at a time. + + filter plugin: + A dpi program that reads from stdin and writes to stdout, and that + exits after its task is done (they don't remain as server plugins). + Warning, dpid will run multiple instances of filter plugins if requested. + +----------- +About dpid: +----------- + + * dpid is a program which manages dpi connections. + * dpid is a daemon that serves dillo using IDS sockets. + * dpid launches dpi programs and arranges socket communication + between the dpi program and dillo. + + The concept and motivation is similar to that of inetd. The +plugin manager (dpid) listens for a service request on a socket +and returns the socket/port pair of a plugin that handles the +service. It also watches sockets of inactive plugins and starts +them when a connection is requested. + + +----------------------------------------------------------- +What's the problem with managing dpi programs inside dillo? +----------------------------------------------------------- + + That's the other way to handle it, but it started to show some +problems (briefly outlined here): + + * When having two or more running instances of Dillo, one + should prevail, and take control of dpi managing (but all + dillos carry the managing code). + * If the managing-dillo exits, it must pass control to another + instance, or leave it void if there's no other dillo running! + * The need to synchronize all the running instances of + dillo arises. + * If the controlling instance finishes and quits, all the + dpi-program PIDs are lost. + * Terminating hanged dpis is hard if it's not done with signals + (PIDs) + * Forks can be expensive (Dillo had to fork its dpis). + * When a managing dillo exits, the new one is no longer the + parent of the forked dpis. + * If Unix domain sockets for the dpis were to be named + randomly, it gets very hard to recover their names if the + controlling instance of dillo exits and another must "take + over" the managing. + * It increments dillo's core size. + * If dillo hangs/crashes, dpi activity is lost (e.g. downloads) + * ... + + That's why the managing daemon scheme was chosen. + + +---------------------- +What does dpid handle? +---------------------- + + It solves all the above mentioned shortcomings and also can do: + + * Multiple dillos: + dpid can communicate and serve more than one instance + of dillo. + + * Multiple dillo windows: + two or more windows of the same dillo instance accessing dpis + at the same time. + + * Different implementations of the same service + dpi programs ("dpis") are just an implementation of a + service. There's no problem in implementing a different one + for the same service (e.g. downloads). + + * Upgrading a service: + to a new version or implementation without requiring + patching dillo's core or even bringing down the dpid. + + + And finally, being aware that this design can support the +following functions is very helpful: + + SCHEME Example + ------------------------------------------------------------ + * "one demand/one response" man, preferences, ... + * "resident while working" downloads, mp3, ... + * "resident until exit request" bookmarks, ... + + * "one client only" cd burner, ... + * "one client per instance" man, ... + * "multiple clients/one instance" downloads, cookies ... + + +-------- +Features +-------- + * Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries + are named .dpi as "bookmarks.dpi" and .filter.dpi as in + "hello.filter.dpi". The ".filter" plugins simply read from stdin + and write to stdout. + * Register/update/remove dpis from list of available dpis when a + 'register_all' command is received. + * dpid terminates when it receives a 'DpiBye' command. + * dpis can be terminated with a 'DpiBye' command. + * dpidc control program for dpid, currently allows register and stop. + + +----- +todo: +----- + + These features are already designed, waiting for implementation: + + * dpidc remove // May be not necessary after all... + + +----------------- +How does it work? +----------------- + +o on startup dpid reads dpidrc for the path to the dpi directory + (usually EPREFIX/lib/dillo/dpi). ~/.dillo/dpi is scanned first. + +o both directories are scanned for the list of available plugins. + ~/.dillo/dpi overrides system-wide dpis. + +o next it creates internet domain sockets for the available plugins and + then listens for service requests on its own socket, + and for connections to the sockets of inactive plugins. + +o dpid returns the port of a plugin's socket when a client (dillo) + requests a service. + +o if the requested plugin is a 'server' then + 1) dpid stops watching the socket for activity + 2) forks and starts the plugin + 3) resumes watching the socket when the plugin exits + +o if the requested plugin is a 'filter' then + 1) dpid accepts the connection + 2) maps the socket fd to stdin/stdout (with dup2) + 3) forks and starts the plugin + 4) continues to watch the socket for new connections + + + + +--------------------------- +dpi service process diagram +--------------------------- + + These drawings should be worth a thousand words! :) + + +(I) + .--- s1 s2 s3 ... sn + | + [dpid] [dillo] + | + '--- srs + + The dpid is running listening on several sockets. + + +(II) + .--- s1 s2 s3 ... sn + | + [dpid] [dillo] + | | + '--- srs ------------------' + + dillo needs a service so it connects to the service request + socket of the dpid (srs) and asks for the socket name of the + required plugin (using dpip). + + +(III) + .--- s1 s2 s3 ... sn + | | + [dpid] | [dillo] + | | | + '--- srs '---------------' + + then it connects to that socket (s3, still serviced by dpid!) + + +(IV) + .--- s1 s2 s3 ... sn + | | + .[dpid] | [dillo] + . | | | + . '--- srs '---------------' + . + .............[dpi program] + + when s3 has activity (incoming data), dpid forks the dpi + program for it... + + +(V) + .--- s1 s2 (s3) ... sn + | + [dpid] [dillo] + | | + '--- srs .---------------' + | + [dpi program] + + ... and lets it "to take over" the socket. + + Once there's a socket channel for dpi and dillo, the whole +communication process takes place until the task is done. When +the dpi program exits, dpid resumes listening on the socket (s3). + + +-------------------------------- +So, how do I make my own plugin? +-------------------------------- + + Maybe the simplest way to get started is to understand a few +concepts and then to use the hands-on method by using/modifying +the hello dpi. It's designed as an example to get developers +started. + + --------- + Concepts: + --------- + + * Dillo plugins work by communicating two processes: dillo + and the dpi. + * The underlying protocol (DPIP) has a uniform API which is + powerful enough for both blocking and nonblocking IO, and + filter or server dpis. + * The simplest example is one-request one-answer (for example + dillo asks for a URL and the dpi sends it). You'll find + this and more complex examples in hello.c + + First, you should get familiar with the hello dpi as a user: + + $dillo dpi:/hello/ + + Once you've played enough with it, start reading the well +commented code in hello.c and start making changes! + + + --------------- + Debugging a dpi + --------------- + + The simplest way is to add printf-like feedback using the MSG* +macros. You can start the dpid by hand on a terminal to force +messages to go there. Filter dpis use sdterr and server dpis +stdout. + + Sometimes more complex dpis need more than MSG*. In this case +you can use gdb like this. + + 1.- Add an sleep(20) statement just after the dpi starts. + 2.- Start dillo and issue a request for your dpi. This will + get your dpi started. + 3.- Standing in the dpi source directory: + ps aux|grep dpi + 4.- Take note of the dpi's PID and start gdb, then: + (gdb) attach + 5.- Continue from there... + + + ------------ + Final Notes: + ------------ + + 1.- If you already understand the hello dpi and want to try +something more advanced: + + * bookmarks.c is a good example of a blocking server + * file.c is an advanced example of a server handling multiple + non-blocking connections with select(). + + 2.- Multiple instances of a filter plugin may be run +concurrently, this could be a problem if your plugin records data +in a file, however it is safe if you simply write to stdout. +Alternatively you could write a 'server' plugin instead as they +are guaranteed not to run concurrently. + + 3.- The hardest part is to try to modify the dpi framework code +inside dillo; you have been warned! It already supports a lot of +functionality, but if you need to do some very custom stuff, try +extending the "chat" command, or asking in dillo-dev. + + + + >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<< + diff --git a/devdoc/HtmlParser.txt b/devdoc/HtmlParser.txt new file mode 100644 index 00000000..2eb8be63 --- /dev/null +++ b/devdoc/HtmlParser.txt @@ -0,0 +1,124 @@ + October 2001, --Jcid + Last update: Jul 2009 + + --------------- + THE HTML PARSER + --------------- + + + Dillo's parser is more than just a HTML parser, it does XHTML +and plain text also. It has parsing 'modes' that define its +behaviour while working: + + typedef enum { + DILLO_HTML_PARSE_MODE_INIT = 0, + DILLO_HTML_PARSE_MODE_STASH, + DILLO_HTML_PARSE_MODE_STASH_AND_BODY, + DILLO_HTML_PARSE_MODE_BODY, + DILLO_HTML_PARSE_MODE_VERBATIM, + DILLO_HTML_PARSE_MODE_PRE + } DilloHtmlParseMode; + + + The parser works upon a token-grained basis, i.e., the data +stream is parsed into tokens and the parser is fed with them. The +process is simple: whenever the cache has new data, it is +passed to Html_write, which groups data into tokens and calls the +appropriate functions for the token type (tag, space, or word). + + Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser +doesn't try to split the data stream into tokens anymore; it +simply collects until the closing tag. + +------ +TOKENS +------ + + * A chunk of WHITE SPACE --> Html_process_space + + + * TAG --> Html_process_tag + + The tag-start is defined by two adjacent characters: + + first : '<' + second: ALPHA | '/' | '!' | '?' + + Note: comments are discarded ( ) + + + The tag's end is not as easy to find, nor to deal with!: + + 1) The HTML 4.01 sec. 3.2.2 states that "Attribute/value + pairs appear before the final '>' of an element's start tag", + but it doesn't define how to discriminate the "final" '>'. + + 2) '<' and '>' should be escaped as '<' and '>' inside + attribute values. + + 3) The XML SPEC for XHTML states: + AttrValue ::== '"' ([^<&"] | Reference)* '"' | + "'" ([^<&'] | Reference)* "'" + + Current parser honors the XML SPEC. + + As it's a common mistake for human authors to mistype or + forget one of the quote marks of an attribute value; the + parser solves the problem with a look-ahead technique + (otherwise the parser could skip significant amounts of + properly-written HTML). + + + + * WORD --> Html_process_word + + A word is anything that doesn't start with SPACE, that's + outside of a tag, up to the first SPACE or tag start. + + SPACE = ' ' | \n | \r | \t | \f | \v + + +----------------- +THE PARSING STACK +----------------- + + The parsing state of the document is kept in a stack: + + class DilloHtml { + [...] + lout::misc::SimpleVector *stack; + [...] + }; + + struct _DilloHtmlState { + CssPropertyList *table_cell_props; + DilloHtmlParseMode parse_mode; + DilloHtmlTableMode table_mode; + bool cell_text_align_set; + DilloHtmlListMode list_type; + int list_number; + + /* TagInfo index for the tag that's being processed */ + int tag_idx; + + dw::core::Widget *textblock, *table; + + /* This is used to align list items (especially in enumerated lists) */ + dw::core::Widget *ref_list_item; + + /* This is used for list items etc; if it is set to TRUE, breaks + have to be "handed over" (see Html_add_indented and + Html_eventually_pop_dw). */ + bool hand_over_break; + }; + + Basically, when a TAG is processed, a new state is pushed into +the 'stack' and its 'style' is set to reflect the desired +appearance (details in DwStyle.txt). + + That way, when a word is processed later (added to the Dw), all +the information is within the top state. + + Closing TAGs just pop the stack. + + diff --git a/devdoc/IO.txt b/devdoc/IO.txt new file mode 100644 index 00000000..cd62a4f5 --- /dev/null +++ b/devdoc/IO.txt @@ -0,0 +1,468 @@ + +This is the updated base of a paper I wrote with Horst. +It provides a good introduction to Dillo's internals. +(Highly recommended if you plan to patch or develop in Dillo) + +It may not be exactly accurate (it's quite old), but it explains +the theory behind in some detail, so it's more than recommended +reading material. +--Jcid + + +----------------------------------------------------- +Parallel network programming of the Dillo web browser +----------------------------------------------------- + + Jorge Arellano-Cid + Horst H. von Brand + + +-------- +Abstract +-------- + + Network programs face several delay sources when sending or +retrieving data. This is particularly problematic in programs +which interact directly with the user, most notably web browsers. +We present a hybrid approach using threads communicated through +pipes and signal driven I/O, which allows a non-blocking main +thread and overlapping waiting times. + + +------------ +Introduction +------------ + + The Dillo project didn't start from scratch but mainly working +on the code base of gzilla (a light web browser written by Raph +Levien). As the project went by, the code of the whole source was +standardized, and the networking engine was replaced with a new, +faster design. Later, the parser was changed, the streamed +handling of data and its error control was put under the control +of the CCC (Concomitant Control Chain), and the old widget system +was replaced with a new one (Dw). The source code is currently +regarded as "very stable beta", and is available at +. Dillo is a project licensed under the GNU +General Public License. + + This paper covers basic design aspects of the hybrid approach +that the Dillo web browser uses to solve several latency +problems. After introducing the main delay-sources, the main +points of the hybrid design will be addressed. + + +------------- +Delay sources +------------- + + Network programs face several delay-sources while sending or +retrieving data. In the particular case of a web browser, they +are found in: + + DNS querying: + The time required to solve a name. + + Initiating the TCP connection: + The three way handshake of the TCP protocol. + + Sending the query: + The time spent uploading queries to the remote server. + + Retrieving data: + The time spent expecting and receiving the query answer. + + Closing the TCP connection: + The four packet-sending closing sequence of the TCP protocol. + + In a WAN context, every single item of this list has an +associated delay that is non deterministic and often measured in +seconds. If we add several connections per browsed page (each one +requiring at least the 4 last steps), the total latency can be +considerable. + + +----------------------------------- +The traditional (blocking) approach +----------------------------------- + + The main problems with the blocking approach are: + + When issuing an operation that can't be completed + immediately, the process is put to sleep waiting for + completion, and the program doesn't do any other + processing in the meantime. + + When waiting for a specific socket operation to complete, + packets that belong to other connections may be arriving, + and have to wait for service. + + Web browsers handle many small transactions, + if waiting times are not overlapped + the latency perceived by the user can be very annoying. + + If the user interface is just put to sleep during network + operations, the program becomes unresponsive, confusing + and perhaps alarming the user. + + Not overlapping waiting times and processing makes + graphical rendering (which is arguably the central function + of a browser) unnecessarily slow. + + +--------------------- +Dillo's hybrid design +--------------------- + + Dillo uses threads and signal driven I/O extensively to +overlap waiting times and computation. Handling the user +interface in a thread that never blocks gives a good interactive +``feel.'' The use of GTK+, a sophisticated widget framework for +graphical user interfaces, helped very much to accomplish this +goal. All the interface, rendering and I/O engine was built upon +its facilities. + + The design is said to be ``hybrid'' because it uses threads +for DNS querying and reading local files, and signal driven I/O +for TCP connections. The threaded DNS scheme is potentially +concurrent (this depends on underlying hardware), while the I/O +handling (both local files and remote connections) is +definitively parallel. + + To simplify the structure of the browser, local files are +encapsulated into HTTP streams and presented to the rest of the +browser as such, in exactly the same way a remote connection is +handled. To create this illusion, a thread is launched. This +thread opens a pipe to the browser, it then synthesizes an +appropriate HTTP header, sends it together with the file to the +browser proper. In this way, all the browser sees is a handle, +the data on it can come from a remote connection or from a local +file. + + To handle a remote connection is more complex. In this case, +the browser asks the cache manager for the URL. The name in the +URL has to be resolved through the DNS engine, a socket TCP +connection must be established, the HTTP request has to be sent, +and finally the result retrieved. Each of the steps mentioned +could give rise to errors, which have to be handled and somehow +communicated to the rest of the program. For performance reasons, +it is critical that responses are cached locally, so the remote +connection doesn't directly hand over the data to the browser; +the response is passed to the cache manager which then relays it +to the rest of the browser. The DNS engine caches DNS responses, +and either answers them from the cache or by querying the DNS. +Querying is done in a separate thread, so that the rest of the +browser isn't blocked by long waits here. + + The activities mentioned do not happen strictly in the order +stated above. It is even possible that several URLs are being +handled at the same time, in order to overlap waiting and +downloading. The functions called directly from the user +interface have to return quickly to maintain interactive +response. Sometimes they return connection handlers that haven't +been completely set up yet. As stated, I/O is signal-driven, when +one of the descriptors is ready for data transfer (reading or +writing), it wakes up the I/O engine. + + Data transfer between threads inside the browser is handled by +pipes, shared memory is little used. This almost obviates the +need for explicit synchronization, which is one of the main areas +of complexity and bugs in concurrent programs. Dillo handles its +threads in a way that its developers can think of it as running +on a single thread of control. This is accomplished by making the +DNS engine call-backs happen within the main thread, and by +isolating file loading with pipes. + + Using threads in this way has three big advantages: + + The browser doesn't block when one of its child threads + blocks. In particular, the user interface is responsive + even while resolving a name or downloading a file. + + Developers don't need to deal with complex concurrent + concerns. Concurrency is hard to handle, and few developers + are adept at this. This gives access a much larger pool of + potential developers, something which can be critical + in an open-source development project. + + By making the code mostly sequential, debugging the code + with traditional tools like gdb is possible. Debugging + parallel programs is very hard, and appropriate tools are + hard to come by. + + Because of simplicity and portability concerns, DNS querying +is done in a separate thread. The standard C library doesn't +provide a function for making DNS queries that don't block. The +alternative is to implement a new, custom DNS querying function +that doesn't block. This is certainly a complex task, integrating +this mechanism into the thread structure of the program is much +simpler. + + Using a thread and a pipe to read a local file adds a +buffering step to the process (and a certain latency), but it has +a couple of significative advantages: + + By handling local files in the same way as remote + connections, a significant amount of code is reused. + + A preprocessing step of the file data can be added easily, + if needed. In fact, the file is encapsulated into an HTTP + data stream. + + +----------- +DNS queries +----------- + + Dillo handles DNS queries with threads, letting a child thread +wait until the DNS server answers the request. When the answer +arrives, a call-back function is called, and the program resumes +what it was doing at DNS-request time. The interesting thing is +that the call-back happens in the main thread, while the child +thread simply exits when done. This is implemented through a +server-channel design. + + +The server channel +------------------ + + There is one thread for each channel, and each channel can +have multiple clients. When the program requests an IP address, +the server first looks for a cached match; if it hits, the client +call-back is invoked immediately, but if not, the client is put +into a queue, a thread is spawned to query the DNS, and a GTK+ +idle client is set to poll the channel 5~times per second for +completion, and when it finally succeeds, every client of that +channel is serviced. + + This scheme allows all the further processing to continue on +the same thread it began: the main thread. + + +------------------------ +Handling TCP connections +------------------------ + + Establishing a TCP connection requires the well known +three-way handshake packet-sending sequence. Depending on network +traffic and several other issues, significant delay can occur at +this phase. + + Dillo handles the connection by a non blocking socket scheme. +Basically, a socket file descriptor of AF_INET type is requested +and set to non-blocking I/O. When the DNS server has resolved the +name, the socket connection process begins by calling connect(2); + {We use the Unix convention of identifying the manual section + where the concept is described, in this case + section 2 (system calls).} +which returns immediately with an EINPROGRESS error. + + After the connection reaches the EINPROGRESS ``state,'' the +socket waits in background until connection succeeds (or fails), +when that happens, a callback function is awaked to perform the +following steps: set the I/O engine to send the query and expect +its answer (both in background). + + The advantage of this scheme is that every required step is +quickly done without blocking the browser. Finally, the socket +will generate a signal whenever I/O is possible. + + +---------------- +Handling queries +---------------- + + In the case of a HTTP URL, queries typically translate into a +short transmission (the HTTP query) and a lengthy retrieval +process. Queries are not always short though, specially when +requesting forms (all the form data is attached within the +query), and also when requesting CGI programs. + + Regardless of query length, query sending is handled in +background. The thread that was initiated at TCP connecting time +has all the transmission framework already set up; at this point, +packet sending is just a matter of waiting for the +write signal (G_IO_OUT) to come and then sending the data. When +the socket gets ready for transmission, the data is sent using +IO_write. + + +-------------- +Receiving data +-------------- + + Although conceptually similar to sending queries, retrieving +data is very different as the data received can easily exceed the +size of the query by many orders of magnitude (for example when +downloading images or files). This is one of the main sources of +latency, the retrieval can take several seconds or even minutes +when downloading large files. + + The data retrieving process for a single file, that began by +setting up the expecting framework at TCP connecting time, simply +waits for the read signal (G_IO_IN). When it happens, the +low-level I/O engine gets called, the data is read into +pre-allocated buffers and the appropriate call-backs are +performed. Technically, whenever a G_IO_IN event is generated, +data is received from the socket file descriptor, by using the +IO_read function. This iterative process finishes upon EOF (or on +an error condition). + + +---------------------- +Closing the connection +---------------------- + + Closing a TCP connection requires four data segments, not an +impressive amount but twice the round trip time, which can be +substantial. When data retrieval finishes, socket closing is +triggered. There's nothing but a IO_close_fd call on the socket's +file descriptor. This process was originally designed to split +the four segment close into two partial closes, one when query +sending is done and the other when all data is in. This scheme is +not currently used because the write close also stops the reading +part. + + +The low-level I/O engine +------------------------ + + Dillo I/O is carried out in the background. This is achieved +by using low level file descriptors and signals. Anytime a file +descriptor shows activity, a signal is raised and the signal +handler takes care of the I/O. + + The low-level I/O engine ("I/O engine" from here on) was +designed as an internal abstraction layer for background file +descriptor activity. It is intended to be used by the cache +module only; higher level routines should ask the cache for its +URLs. Every operation that is meant to be carried out in +background should be handled by the I/O engine. In the case of +TCP sockets, they are created and submitted to the I/O engine for +any further processing. + + The submitting process (client) must fill a request structure +and let the I/O engine handle the file descriptor activity, until +it receives a call-back for finally processing the data. This is +better understood by examining the request structure: + + typedef struct { + gint Key; /* Primary Key (for klist) */ + gint Op; /* IORead | IOWrite | IOWrites */ + gint FD; /* Current File Descriptor */ + gint Flags; /* Flag array */ + glong Status; /* Number of bytes read, or -errno code */ + + void *Buf; /* Buffer place */ + size_t BufSize; /* Buffer length */ + void *BufStart; /* PRIVATE: only used inside IO.c! */ + + void *ExtData; /* External data reference (not used by IO.c) */ + void *Info; /* CCC Info structure for this IO */ + GIOChannel *GioCh; /* IO channel */ + guint watch_id; /* glib's event source id */ + } IOData_t; + + To request an I/O operation, this structure must be filled and +passed to the I/O engine. + + 'Op' and 'Buf' and 'BufSize' MUST be provided. + + 'ExtData' MAY be provided. + + 'Status', 'FD' and 'GioCh' are set by I/O engine internal +routines. + + When there is new data in the file descriptor, 'IO_callback' +gets called (by glib). Only after the I/O engine finishes +processing the data are the upper layers notified. + + +The I/O engine transfer buffer +------------------------------ + + The 'Buf' and 'BufSize' fields of the request structure +provide the transfer buffer for each operation. This buffer must +be set by the client (to increase performance by avoiding copying +data). + + On reads, the client specifies the amount and where to place +the retrieved data; on writes, it specifies the amount and source +of the data segment that is to be sent. Although this scheme +increases complexity, it has proven very fast and powerful. For +instance, when the size of a document is known in advance, a +buffer for all the data can be allocated at once, eliminating the +need for multiple memory reallocations. Even more, if the size is +known and the data transfer is taking the form of multiple small +chunks of data, the client only needs to update 'Buf' and +BufSize' to point to the next byte in its large preallocated +reception buffer (by adding the chunk size to 'Buf'). On the +other hand, if the size of the transfer isn't known in advance, +the reception buffer can remain untouched until the connection +closes, but the client must then accomplish the usual buffer +copying and reallocation. + + The I/O engine also lets the client specify a full length +transfer buffer when sending data. It doesn't matter (from the +client's point of view) if the data fits in a single packet or +not, it's the I/O engine's job to divide it into smaller chunks +if needed and to perform the operation accordingly. + + +------------------------------------------ +Handling multiple simultaneous connections +------------------------------------------ + + The previous sections describe the internal work for a single +connection, the I/O engine handles several of them in parallel. +This is the normal downloading behavior of a web page. Normally, +after retrieving the main document (HTML code), several +references to other files (typically images) and sometimes even +to other sites (mostly advertising today) are found inside the +page. In order to parse and complete the page rendering, those +other documents must be fetched and displayed, so it is not +uncommon to have multiple downloading connections (every one +requiring the whole fetching process) happening at the same time. + + Even though socket activity can reach a hectic pace, the +browser never blocks. Note also that the I/O engine is the one +that directs the execution flow of the program by triggering a +call-back chain whenever a file descriptor operation succeeds or +fails. + + A key point for this multiple call-back chained I/O engine is +that every single function in the chain must be guaranteed to +return quickly. Otherwise, the whole system blocks until it +returns. + + +----------- +Conclusions +----------- + + Dillo is currently in very stable beta state. It already shows +impressive performance, and its interactive ``feel'' is much +better than that of other web browsers. + + The modular structure of Dillo, and its reliance on GTK1 allow +it to be very small. Not every feature of HTML-4.01 has been +implemented yet, but no significant problems are foreseen in +doing this. + + The fact that Dillo's central I/O engine is written using +advanced features of POSIX and TCP/IP networking makes its +performance possible, but on the other hand this also means that +only a fraction of the interested hackers are able to work on it. + + A simple code base is critical when trying to attract hackers +to work on a project like this one. Using the GTK+ framework +helped both in creating the graphical user interface and in +handling the concurrency inside the browser. By having threads +communicate through pipes the need for explicit synchronization +is almost completely eliminated, and with it most of the +complexity of concurrent programming disappears. + + A clean, strictly applied layering approach based on clear +abstractions is vital in each programming project. A good, +supportive framework is of much help here. + + diff --git a/devdoc/Images.txt b/devdoc/Images.txt new file mode 100644 index 00000000..62082e48 --- /dev/null +++ b/devdoc/Images.txt @@ -0,0 +1,129 @@ + January 2009, --Jcid + +Update June 2015: See also doc/dw-images-and-backgrounds.doc, or +../html/dw-images-and-backgrounds.html (generated by doxygen). + + ------ + IMAGES + ------ + +* When a image tag is found within a HTML page, Html_tag_open_img +handles it by: + + - Parsing & getting attribute values. + - Creating a new image structure (DilloImage) and its + associated widget (DwImage). + i.e. If 'Image' is the var for the structure, then + 'Image->dw' is the widget. + - Requesting the image to be feeded by the cache. + - Sending some info to the browser interface. + +* The cache can either request the image data from the net, or +feed it directly from the dicache (decompressed image cache). + +* Both processes are somewhat different because the first one +requires to decode the image data into RGB format, and the second +one has the whole data already decoded. + +* Regardless of the RGB-data feeding method, the decoded data is +passed to the widget (DwImage) and drawn in a streamed way. + Note that INDEXED images are also decoded into RGB format. + + Html_tag_open_img // IMG element processing + Html_add_new_image // Read attributes, create image, add to HTML page + a_Image_new // Create a 'DilloImage' data structure, to coordinate + // decoded image-data transfer to an 'Imgbuf'. + Html_add_widget // Adds the dw::Image to the page + Html_load_image // Tells cache to retrieve image + + +--------------------- +Fetching from the net +--------------------- + +* a_Capi_open_url initiates the resource request, and when +finally the answer arrives, the HTTP header is examined for MIME +type and either the GIF or PNG or JPEG decoder is set to handle +the incoming data stream. + + Decoding functions: + a_Gif_callback, a_Jpeg_callback and a_Png_callback. + +* The decoding function calls the following dicache methods as +the data is processed (listed in order): + + a_Dicache_set_parms + a_Dicache_set_cmap (only for indexed-GIF images) + a_Dicache_write + a_Dicache_new_scan (MAY be called here or after set_cmap) + a_Dicache_close + + +* The dicache methods call the necessary functions to connect +with the widget code. This is done by calling image.c functions: + + a_Image_set_parms + a_Image_set_cmap + a_Image_write + a_Image_new_scan + a_Image_close + +* The functions in image.c make the required Dw calls. + + +------------------------- +Fetching from the dicache +------------------------- + +* a_Capi_open_url() tests the cache for the image, and the cache, +via a_Cache_open_url(), enqueues a client for it, without asking +the network for the data. When the client queue is processed (a +bit later), Dicache_image() is set as the callback. + +* When Dicache_image() is called, it sets the proper image data +decoder (RGB) and its data structure based on the entry's Type. +Then it substitutes itself with a_Dicache_callback() as the +handling function, and gets out of the way. + +* Thenceforth the rest of the functions calls is driven by +a_Dicache_callback(). + + +----------- +Misc. notes +----------- + +* Repeated images generate new cache clients, but they may share +the imgbuf. + Note: Currently there's no proper support for transparent +images (i.e. decode to RGBA), but most of the time they render +the background color OK. This is: when first loaded, repeated +images share a background color, but when cached they render +correctly ;-). There's no point in trying to fix this because the +correct solution is to decode to RGBA and let the toolkit (FLTK) +handle the transparency. + +* The first cache-client callback (Dicache_image()) is set when +the Content-type of the image is got. + +* Later on, when there's a shared imgbuf, the dicache's logic +avoids decoding it multiple times and reuses what's already done. + +* The dicache-entry and the Image structure hold bit arrays that +represent which rows have been decoded. + +* The image processing can be found in the following sources: + + - image.{cc,hh} + - dicache.[ch] + - gif.[ch], png.[ch], jpeg.[ch] + - dw/image.{cc,hh} + +* Bear in mind that there are four data structures for image +code: + + - DilloImage (image.hh) + - DICacheEntry (dicache.h) + - dw::Image (class Image in dw/image.hh) + - core::Imgbuf (imgbuf.hh) + diff --git a/devdoc/NC_design.txt b/devdoc/NC_design.txt new file mode 100644 index 00000000..380787f6 --- /dev/null +++ b/devdoc/NC_design.txt @@ -0,0 +1,127 @@ + + _________________________________________________________________ + + Naming&Coding design + _________________________________________________________________ + + Dillo's code is divided into modules. For instance: bookmark, cache, + dicache, gif. + + Let's think of a module named "menu", then: + * Every internal routine of the module, should start with "Menu_" + prefix. + * "Menu_" prefixed functions are not meant to be called from outside + the module. + * If the function is to be exported to other modules (i.e. it will + be called from the outside), it should be wrapped with an "a_" + prefix. + + For instance: if the function name is "Menu_create", then it's an + internal function, but if we need to call it from the outside, then it + should be renamed to "a_Menu_create". + + Why the "a_" prefix? + Because of historical reasons. + And "a_Menu_create" reads better than "d_Menu_create" because the + first one suggests "a Menu create" function! + + Another way of understanding this is thinking of "a_" prefixed + functions as Dillo's internal library, and the rest ("Menu_" prefixed + in our example) as a private module-library. + + Indentation: + + Source code must be indented with 3 blank spaces, no Tabs. + Why? + Because different editors expand or treat tabs in several ways; 8 + spaces being the most common, but that makes code really wide and + we'll try to keep it within the 80 columns bounds (printer friendly). + + You can use: indent -kr -sc -i3 -bad -nbbo -nut -l79 myfile.c + + Function commenting: + + Every single function of the module should start with a short comment + that explains its purpose; three lines must be enough, but if you + think it requires more, enlarge it. + + /* + * Try finding the url in the cache. If it hits, send the contents + * to the caller. If it misses, set up a new connection. + */ + int a_Cache_open_url(const char *url, void *Data) + { + ... + ... + ... + } + + We also have the BUG:, TODO:, and WORKAROUND: tags. + Use them within source code comments to spot hidden issues. For + instance: + + /* BUG: this counter is not accurate */ + ++i; + + /* TODO: get color from the right place */ + a = color; + + /* WORKAROUND: the canonical way of doing it doesn't work yet. */ + ++a; ++a; ++a; + + Function length: + + Let's try to keep functions within the 45 lines boundary. This eases + code reading, following, understanding and maintenance. + + Functions with a single exit: + + It's much easier to follow and maintain functions with a single exit + point at the bottom (instead of multiple returns). The exception to + the rule are calls like dReturn_if_fail() at its head. + + dlib functions: + + * Dillo uses dlib extensively in its C sources. Before starting + to code something new, a good starting point is to check what + this library has to offer (check dlib/dlib.h). + * Memory management must be done using dNew, dNew0, dMalloc, dFree + and their relatives. + * For debugging purposes and error catching (not for normal flow): + dReturn_if_fail, dReturn_val_if_fail etc. are encouraged. + * The MSG macro is extensively used to output additional information + to the calling terminal. + + _________________________________________________________________ + + C++ + + Source code in C++ should follow the same rules with these exceptions: + + * Class method names are camel-cased and start with lowercase + e.g. appendInputMultipart + * Classes and types start uppercased + e.g. class DilloHtmlReceiver + * Class methods don't need to prefix its module name + e.g. links->get() + + We also try to keep the C++ relatively simple. Dillo does use + inheritance and templates, but that's about all. + + _________________________________________________________________ + + What do we get with this? + + * A clear module API for Dillo; every function prefixed "a_" is to + be used outside the module. + * A way to identify where the function came from (the + capitalized word is the module name). + * An inner ADT (Abstract data type) for the module that can be + isolated, tested and replaced independently. + * A two stage instance for bug-fixing. You can change the exported + function algorithms while someone else fixes the internal + module-ADT! + * A coding standard ;) + _________________________________________________________________ + + Naming&Coding design by Jorge Arellano Cid diff --git a/devdoc/README b/devdoc/README new file mode 100644 index 00000000..9736a32b --- /dev/null +++ b/devdoc/README @@ -0,0 +1,51 @@ +README: Last update Jul 2009 + +These documents cover dillo's internals. +For user help, see http://www.dillo.org/dillo3-help.html + +-------------------------------------------------------------------------- + +These documents need a review. +*.txt were current with Dillo1, but many have since become more or + less out-of-date. +*.doc are doxygen source for the Dillo Widget (dw) component, and + were written for Dillo2. + +They will give you an overview of what's going on, but take them +with a pinch of salt. + + Of course I'd like to have *.txt as doxygen files too! +If somebody wants to make this conversion, please let me know +to assign higher priority to updating these docs. + +-- +Jorge.- + + -------------------------------------------------------------------------- + FILE DESCRIPTION STATE + -------------------------------------------------------------------------- + NC_design.txt Naming&Coding design (Be sure to Current + read it before any other doc) + Dillo.txt General overview of the program Current + IO.txt Extensive introduction Current + Cache.txt Informative description Current + Images.txt Image handling and processing Current + HtmlParser.txt A versatile parser Current + Dw.txt The New Dillo Widget (Overview) Current + Imgbuf.txt Image buffers Pending + Selection.txt Selections, and link activation Current (?) + Cookies.txt Explains how to enable cookies Current + Dpid.txt Dillo plugin daemon Current + -------------------------------------------------------------------------- + + + * BTW, there's a small program (srch) within the src/ dir. It searches + tokens within the whole code (*.[ch]). It has proven very useful. + Ex: ./srch a_Image_write + ./srch todo: + + * Please submit your patches with 'hg diff'. + + + Happy coding! + --Jcid diff --git a/devdoc/dw-changes.doc b/devdoc/dw-changes.doc new file mode 100644 index 00000000..7050df9a --- /dev/null +++ b/devdoc/dw-changes.doc @@ -0,0 +1,105 @@ +/** \page dw-changes Changes to the GTK+-based Release Version + +

Changes in Dw

+ +Related to the FLTK port, there have been many changes, this is a +(hopefully complete) list: + +
    +
  • Rendering abstraction, read \ref dw-overview and \ref dw-layout-views + for details. Some important changes: + +
      +
    • The underlying platform (e.g. the UI toolkit) is fully abstract, + there are several platform independent structures replacing + GTK+ structures, e.g. dw::core::Event. + +
    • The central class managing the widget tree is not anymore + GtkDwViewport, but dw::core::Layout. + +
    • Drawing is done via dw::core::View, a pointer is passed to + dw::core::Widget::draw. + +
    • The distinction between viewport coordinates and canvas + coordinates (formerly world coordinates) has been mostly + removed. (Only for views, it sometimes plays a role, see + \ref dw-layout-views). +
    + +
  • Cursors have been moved to dw::core::style, see + dw::core::style::Style::cursor. dw::core::Widget::setCursor is now + protected (and so only called by widget implementations). + +
  • World coordinates are now called canvas coordinates. + +
  • There is now a distinction between dw::core::style::StyleAttrs and + dw::core::style::Style. + +
  • There is no base class for container widgets anymore. The former + DwContainer::for_all has been removed, instead this functionality + is now done via iterators (dw::core::Widget::iterator, + dw::core::Iterator). + +
  • DwPage is now called dw::Textblock, and DwAlignedPage + dw::AlignedTextblock. + +
  • dw::Textblock, all sub classes of it, and dw::Table do not read + "limit_text_width" from the preferences, but get it as an argument. + (May change again.) + +
  • dw::Table has been rewritten. + +
  • Instead of border_spacing in the old DwStyle, there are two attributes, + dw::core::style::Style::hBorderSpacing and + dw::core::style::Style::vBorderSpacing, since CSS allowes to specify + two values. Without CSS, both attributes should have the same value. + +
  • Images are handled differently, see \ref dw-images-and-backgrounds. + +
  • Embedded UI widgets (formerly GtkWidget's) are handled differently, + see dw::core::ui. + +
  • DwButton has been removed, instead, embedded UI widgets are used. See + dw::core::ui and dw::core::ui::ComplexButtonResource. +
+ +Dw is now written C++, the transition should be obvious. All "Dw" +prefixes have been removed, instead, namespaces are used now: + +
    +
  • dw::core contains the core, +
  • dw::core::style styles, +
  • dw::core::ui embedded UI resources, +
  • dw::fltk classes related to FLTK, and +
  • ::dw the widgets. +
+ +

Documentation

+ +The old documentation has been moved to: + + +
Old New +
Dw.txt + general part \ref dw-overview, \ref dw-usage, + \ref dw-layout-widgets, + \ref dw-widget-sizes +
remarks on specific widgets respective source files: dw::Bullet, + dw::core::ui::Embed +
DwImage.txt + signals dw::core::Layout::LinkReceiver +
rest dw::Image, + \ref dw-images-and-backgrounds +
Imgbuf.txt dw::core::Imgbuf, + \ref dw-images-and-backgrounds +
DwPage.txt dw::Textblock +
DwRender.txt \ref dw-overview, \ref dw-layout-views, + dw::core::ui +
DwStyle.txt dw::core::style +
DwTable.txt dw::Table +
DwWidget.txt dw::core::Widget, \ref dw-layout-widgets, + \ref dw-widget-sizes +
Selection.txt dw::core::SelectionState +
+ +*/ diff --git a/devdoc/dw-example-screenshot.png b/devdoc/dw-example-screenshot.png new file mode 100644 index 00000000..94f272ab Binary files /dev/null and b/devdoc/dw-example-screenshot.png differ diff --git a/devdoc/dw-floats-01.png b/devdoc/dw-floats-01.png new file mode 100644 index 00000000..116d36b3 Binary files /dev/null and b/devdoc/dw-floats-01.png differ diff --git a/devdoc/dw-grows.doc b/devdoc/dw-grows.doc new file mode 100644 index 00000000..a0304ef9 --- /dev/null +++ b/devdoc/dw-grows.doc @@ -0,0 +1,202 @@ +/** \page dw-grows GROWS - Grand Redesign Of Widget Sizes + +This paper describes (will describe) some design changes to +calculating widget sizes. Goals are: + +- Simplification of widget size calculation by the parent widget; + dw::Textblock::calcWidgetSize, dw::OutOfFlowMgr::ensureFloatSize + etc. should become simpler or perhaps even obsolete. + +- Making the implementation of some features possible: + + - *max-width*, *max-height*, *min-width*, *min-height*; + - correct aspect ratio for images with only one percentage size defined; + - *display: inline-block*; + - <button>. + + +A short sketch +============== + +**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will +return final results.** The caller does not have to correct the size, +e. g. when percentages are defined. As an example, +dw::Textblock::calcWidgetSize has already become much simpler. + +**A new hierarchy, *container*:** Aside from dw::core::Widget::parent +and dw::core::Widget::generator, there is a third hierarchy +dw::core::Widget::container, which is (unlike *generator*) always a +direct ancestor, and represents what in CSS is called *containing +block*. Containers are important to define the "context size", which +is (not solely) used for percentage sizes. + +(There is another "containing block", dw::Textblock::containingBlock; +these may be consolidated some day.) + +**The process of size calculation is split between the widget itself +and its container:** + +- The container provides some abstract methods: + dw::core::Widget::getAvailWidthOfChild, + dw::core::Widget::getAvailHeightOfChild, + dw::core::Widget::correctRequisitionOfChild, and + dw::core::Widget::correctExtremesOfChild, which can be used in the + actual implementation of dw::core::Widget::sizeRequestImpl; + different containers with different ways how to arrange their + children will implement these methods in a different way. (Simple + example: the *available width* for children within a textblock is + the *available width* for the textblock itself, minus + margin/border/padding; on the other hand, it is completely different + for children of tables, for which a complex column width calculation + is used.) + +- The actual size calculation is, however, controlled by the widget + itself, which only *uses* these methods above. + +
+ Update: This is not fully correct; the parents are also involved + for calculating available widths and heights, at least when CSS 'width' + and 'height' are not set.
+ +**Size hints are removed.** Instead, the container methods in the +previous paragraph are used. Changes of container sizes (especially +viewport the size) are handled in a different way. + +**Extremes are extended by intrinsic values.** In some cases (see +dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an +example) it is useful to know about minimal and maximal width of a +widget independent of CSS attributes. For this, dw::core::Extremes is +extended by: + +- dw::core::Extremes::minWidthIntrinsic and +- dw::core::Extremes::maxWidthIntrinsic. + +The rules for the calculation: + +1. If a widget has no children, it calculates *minWidthIntrinsic* and + *maxWidthIntrinsic* as those values not affected by CSS hints. + (dw::core::Widget::correctExtremes will not change these values.) +2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic* + from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children, + and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its + children. +3. At the end, *minWidth* and *maxWidth* of a widget are corrected by + CSS attributes. (dw::core::Widget::correctExtremes will do this.) + +
+ Notice: Currently, dw::core::Widget::getExtremesImpl must + set all four members in dw::core::Extremes; this may change.
+ +Another **extension of extremes: *adjustmentWidth*.** This is used as +minimum for the width, when "adjust_min_width" (or, +"adjust_table_min_width", respectively) is set. + +The rules for the calculation: + +1. If a widget has no children, it can choose a suitable value, + typically based on dw::core::Extremes::minWidth and + dw::core::Extremes::minWidthIntrinsic. +2. A widget must calculate *adjustmentWidth* from *adjustmentWidth* of + its children. + +*Note:* An implementation of dw::core::Widget::getExtremesImpl may set +this value *after* calling dw::core::Widget::correctExtremesOfChild, +so that it cannot be used for the correction of extremes. In this case +*useAdjustmentWidth = false* should be passed to +dw::core::Widget::correctExtremesOfChild. On the other hand, if known +before, *useAdjustmentWidth* should be set to *true*. + +Rules for *new* methods related to resizing +=========================================== + +- Of course, *sizeRequestImpl* may (should) call *correctRequisition*, + and *getExtremesImpl* may (should) call *correctExtremes*. + +- *sizeRequestImpl* (and *correctRequisition*) is allowed to call + *getAvailWidth* and *getAvailHeight* with *forceValue* set, but + *getExtremesImpl* (and *correctExtremes*) is allowed to call these + only with *forceValue* unset. + +- For this reason, *sizeRequestImpl* is indeed allowed to call + *getExtremes* (dw::Table does so), but the opposite + (*getExtremesImpl* calling *sizeRequest*) is not allowed + anymore. (Before GROWS, the standard implementation + dw::core::Widget::getExtremesImpl did so.) + +- Finally, *getAvailWidth* and *getAvailHeight* may call + *getExtremes*, if and only if *forceValue* is set. + +Here is a diagram showing all permitted dependencies: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"]; + edge [arrowhead="open", arrowtail="none", color="#404040"]; + + "sizeRequest[Impl]" -> "getExtremes[Impl]"; + "sizeRequest[Impl]" -> correctRequisition; + "getExtremes[Impl]" -> correctExtremes; + "sizeRequest[Impl]" -> "getAvail[Width|Height] (true)"; + "getExtremes[Impl]" -> "getAvail[Width|Height] (false)"; + correctRequisition -> "getAvail[Width|Height] (true)"; + correctExtremes -> "getAvail[Width|Height] (false)"; + "getAvail[Width|Height] (true)" -> "getExtremes[Impl]"; +} +\enddot + +Open issues +=========== + +**Do CSS size dimensions override intrinsic sizes in all cases?** If a +textblock needs at least, say, 100 pixels width so that the text can +be read, but has a specification "width: 50px", should half of the +text be invisible? Or should the width be corrected again to 100 +pixels? + +Currently, in the CSS size specification is honoured in all cases, +with one exception: see dw::Textblock::sizeRequestImpl and see +dw::Textblock::getExtremesImpl (the time when +dw::core::Widget::correctRequisition and +dw::core::Widget::correctExtremes, respectively, is called). + +*Not* honouring the CSS size specification in all cases could improve +readability in some cases, so this could depend on a user preference. + +**Update:** There is now a dillorc option adjust_min_width, +which is implemented for widths, but not heights (since it is based on +width extremes, but there are currently no height extremes). + +Another problem is that in most cases, there is no clippping, so that +contents may exceed the allocation of the widget, but redrawing is not +necessarily triggered. + +**Percentage values for margins and paddings, as well as negative +margins** are interesting applications, but have not been considered +yet. For negative margins, a new attribute +dw::core::Widget::extraSpace could solve the problem of widgets +sticking out of the allocation of parent. + +**Clarify percentage heights.** Search in widget.cc, and compare +section 10.5 ('height') of the CSS 2.1 specification to section 10.2 +('width'). + +**Fast queue resize does not work fully.** Example: run +*test/dw-simple-container-test* (dw_simple_container_test.cc), resize +(best maximize) the window and follow (e. g. by using RTFL) what +happens in consequence of dw::core::Layout::viewportSizeChanged. The +dw::SimpleContainer in the middle is not affected, so only the two +dw::Textblock's (at the top and at the bottom) call queueResize with +*fast = true*, and so get *NEEDS_RESIZE* set; but since it is not set +for the dw::SimpleContainer, *sizeRequest* is never called for the +bottom dw::Textblock. + +There does not seem to be a real case for this problem in dillo, since +all widgets which may contain other widgets (except +dw::SimpleContainer, which is not used outside tests) use the +available width and height (dw::core::Widget::usesAvailWidth and +dw::core::Widget::usesAvailHeight), and so are always affected by +viewport size changes. + +*/ diff --git a/devdoc/dw-images-and-backgrounds.doc b/devdoc/dw-images-and-backgrounds.doc new file mode 100644 index 00000000..8f07766a --- /dev/null +++ b/devdoc/dw-images-and-backgrounds.doc @@ -0,0 +1,235 @@ +/** \page dw-images-and-backgrounds Images and Backgrounds in Dw + +Image Buffers +============= + +Representation of the image data is done by dw::core::Imgbuf, see +there for details. Drawing is done by dw::core::View +(dw::core::View::drawImage). + +Since dw::core::Imgbuf provides memory management based on reference +counting, there may be an 1-to-n relation from image renderers (image +widgets or backgrounds, see below) and dw::core::Imgbuf. Since +dw::core::Imgbuf does not know about renderers, but just provides +rendering functionality, the caller must (typically after calling +dw::core::Imgbuf::copyRow) notify all renderers connected to the +buffer. + + +Image Renderer +============== + +Generally, there are no restrictions on how to manage +dw::core::Imgbuf; but to handle image data from web resources, the +interface dw::core::ImgRenderer should be implemented. It is again +wrapped by DilloImage (to make access from the C part possible, since +dw::core::ImgRenderer is written in C++), which is referenced by +DilloWeb. There are two positions where retrieving image data is +initiated: + +- Html_load_image: for embedded images (implemented by dw::Image, + which implements dw::core::ImgRenderer); +- StyleEngine::apply (search for "case + CSS_PROPERTY_BACKGROUND_IMAGE"): for backgrond images; there are + some implementations of dw::core::ImgRenderer within the context of + dw::core::style::StyleImage. + +Both are described in detail below. Notice that the code is quite +similar; only the implementation of dw::core::ImgRenderer differs. + +At this time, dw::core::ImgRenderer has got two methods (see more +documentation there): + +- dw::core::ImgRenderer::setBuffer, +- dw::core::ImgRenderer::drawRow, +- dw::core::ImgRenderer::finish, and +- dw::core::ImgRenderer::fatal. + + +Images +====== + +This is the simplest renderer, displaying an image. For each row to be +drawn, + +- first dw::core::Imgbuf::copyRow, and then +- for each dw::Image, dw::Image::drawRow must be called, with the same + argument (no scaling is necessary). + +dw::Image automatically scales the dw::core::Imgbuf, the root buffer +should be passed to dw::Image::setBuffer. + +\see dw::Image for more details. + + +Background Images +================= + +Since background images are style resources, they are associated with +dw::core::style::Style, as dw::core::style::StyleImage, which is +handled in a similar way (reference counting etc.) as +dw::core::style::Color and dw::core::style::Font, although it is +concrete and not platform-dependant. + +The actual renderer (passed to Web) is an instance of +dw::core::ImgRendererDist (distributes all calls to a set of other +instances of dw::core::ImgRenderer), which contains two kinds of +renderers: + +- one instance of dw::core::style::StyleImage::StyleImgRenderer, which + does everything needed for dw::core::style::StyleImage, and +- other renderers, used externally (widgets etc.), which are added by + dw::core::style::StyleImage::putExternalImgRenderer (and removed by + dw::core::style::StyleImage::removeExternalImgRenderer). + +This diagram gives an comprehensive overview: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="open", dir="both", arrowtail="none", + labelfontname=Helvetica, labelfontsize=10, color="#404040", + labelfontcolor="#000080"]; + fontname=Helvetica; fontsize=10; + + subgraph cluster_dw_style { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + + Style [URL="\ref dw::core::style::Style"]; + StyleImage [URL="\ref dw::core::style::StyleImage"]; + Imgbuf [URL="\ref dw::core::Imgbuf", color="#a0a0a0"]; + StyleImgRenderer + [URL="\ref dw::core::style::StyleImage::StyleImgRenderer"]; + ImgRenderer [URL="\ref dw::core::ImgRenderer", color="#ff8080"]; + ImgRendererDist [URL="\ref dw::core::ImgRendererDist"]; + ExternalImgRenderer + [URL="\ref dw::core::style::StyleImage::ExternalImgRenderer", + color="#a0a0a0"]; + ExternalWidgetImgRenderer + [URL="\ref dw::core::style::StyleImage::ExternalWidgetImgRenderer", + color="#a0a0a0"]; + } + + subgraph cluster_dw_layout { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + + Layout [URL="\ref dw::core::Layout"]; + LayoutImgRenderer [URL="\ref dw::core::Layout::LayoutImgRenderer"]; + } + + subgraph cluster_dw_widget { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + + Widget [URL="\ref dw::core::Widget", color="#a0a0a0"]; + WidgetImgRenderer [URL="\ref dw::core::Widget::WidgetImgRenderer"]; + } + + subgraph cluster_dw_textblock { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + + Textblock [URL="\ref dw::Textblock"]; + Word [URL="\ref dw::Textblock::Word"]; + WordImgRenderer [URL="\ref dw::Textblock::WordImgRenderer"]; + SpaceImgRenderer [URL="\ref dw::Textblock::SpaceImgRenderer"]; + } + + Style -> StyleImage [headlabel="*", taillabel="1"]; + StyleImage -> Imgbuf [headlabel="*", taillabel="1"]; + StyleImage -> StyleImgRenderer [headlabel="1", taillabel="1"]; + StyleImage -> ImgRendererDist [headlabel="1", taillabel="1"]; + ImgRendererDist -> StyleImgRenderer [headlabel="1", taillabel="1"]; + ImgRendererDist -> ImgRenderer [headlabel="1", taillabel="*"]; + + ImgRenderer -> ImgRendererDist [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; + ImgRenderer -> StyleImgRenderer [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; + ImgRenderer -> ExternalImgRenderer [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; + ExternalImgRenderer -> ExternalWidgetImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + + Layout -> LayoutImgRenderer [headlabel="1", taillabel="0..1"]; + ExternalImgRenderer -> LayoutImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + + Widget -> WidgetImgRenderer [headlabel="1", taillabel="0..1"]; + ExternalWidgetImgRenderer -> WidgetImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + + Textblock -> Word [headlabel="1", taillabel="*"]; + Word -> WordImgRenderer [headlabel="1", taillabel="0..1"]; + Word -> SpaceImgRenderer [headlabel="1", taillabel="0..1"]; + ExternalWidgetImgRenderer -> WordImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + WordImgRenderer -> SpaceImgRenderer [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; +} +\enddot + +
[\ref uml-legend "legend"]
+ + +Memory management +----------------- + +dw::core::style::StyleImage extends lout::signal::ObservedObject, so +that deleting this instance can be connected to code dealing with +cache clients etc. See StyleImageDeletionReceiver and how it is +attached in StyleEngine::apply ("case CSS_PROPERTY_BACKGROUND_IMAGE"). + + +Bugs and Things Needing Improvement +=================================== + +(Mostly related to image backgrounds, when not otherwise mentioned.) + +High Priority +------------- + +**Configurability, security/privacy aspects, etc.,** which are +currently available for image widgets, should be adopted. Perhaps some +more configuration options specially for background images. + + +Medium Priority +--------------- + +**Background-attachment** is not yet implemented, and will be postponed. + +**Calls to dw::core::ImgRenderer::fatal** are incomplete. As an +example, it is not called, when connecting to a server fails. (And so, +as far as I see, no cache client is started.) + + +Low Priority +------------ + +**Alpha support:** (not related to image backgrounds) currently alpha +support (and also colormap management) is done in dicache, while +dw::Image is only created with type RGB. This leads to several problems: + +- One dicache entry (representing an image related to the same URL), + which has only one background color, may refer to different images + with different background colors. +- The dicache only handles background colors, not background images. + +The solution is basicly simple: keep alpha support out of dicache; +instead implement RGBA in dw::Image. As it seems, the main problem is +alpha support in FLTK/X11. + + +Solved (Must Be Documented) +--------------------------- + +*Drawing background images row by row may become slow. As an +alternative, dw::core::ImgRenderer::finish could be used. However, +drawing row by row could become an option.* There is now +dw::core::style::drawBackgroundLineByLine, which can be changed in the +code, and is set to *false*. The old code still exists, so changing +this to *true* activates again drawing line by line. + +(For image widgets, this could also become an option: in contexts, +when image data is retrieved in a very fast way.) + +*/ diff --git a/devdoc/dw-layout-views.doc b/devdoc/dw-layout-views.doc new file mode 100644 index 00000000..d1118489 --- /dev/null +++ b/devdoc/dw-layout-views.doc @@ -0,0 +1,256 @@ +/** \page dw-layout-views Layout and Views + +Rendering of Dw is done in a way resembling the model-view pattern, at +least formally. Actually, the counterpart of the model, the layout +(dw::core::Layout), does a bit more than a typical model, namely the +layouting (delegated to the widget tree, see \ref dw-layout-widgets), +and the view does a bit less than a typical view, i.e. only the actual +drawing. + +Additionally, there is a structure representing common properties of +the platform. A platform is typically related to the underlying UI +toolkit, but other uses may be thought of. + +This design helps to archieve two important goals: + +
    +
  • Abstraction of the actual drawing, by different implementations + of dw::core::View. + +
  • It makes portability simple. +
+ + +

Viewports

+ +Although the design implies that the usage of viewports should be +fully transparent to the layout module, this cannot be fully achieved, +for the following reasons: + +
    +
  • Some features, which are used on the level of dw::core::Widget, + e.g. anchors, refer to scrolling positions. + +
  • Size hints (see \ref dw-layout-widgets) depend on the viewport + sizes, e.g. when the user changes the window size, and so also + the size of a viewport, the text within should be rewrapped. +
+ +Therefore, dw::core::Layout keeps track of the viewport size, the +viewport position, and even the thickness of the scrollbars, they are +relevant, see below for more details. +If a viewport is not used, however, the size is not defined. + +Whether a given dw::core::View implementation is a viewport or not, is +defined by the return value of dw::core::View::usesViewport. If this +method returns false, the following methods need not to be implemented +at all: + +
    +
  • dw::core::View::getHScrollbarThickness, +
  • dw::core::View::getVScrollbarThickness, +
  • dw::core::View::scrollTo, and +
  • dw::core::View::setViewportSize. +
+ +

Scrolling Positions

+ +The scrolling position is the canvas position at the upper left corner +of the viewport. Views using viewports must + +
    +
  1. change this value on request (dw::core::View::scrollTo), and +
  2. tell other changes to the layout, e.g. caused by user events + (dw::core::Layout::scrollPosChanged). +
+ +Applications of scrolling positions (anchors, test search etc.) are +handled by the layout, in a way fully transparent to the view. + +

Scrollbars

+ +A feature of the viewport size model are scrollbars. There may be a +vertical scrollbar and a horizontal scrollbar, displaying the +relationship between canvas and viewport height or width, +respectively. If they are not needed, they are hidden, to save screen +space. + +Since scrollbars decrease the usable space of a view, dw::core::Layout +must know how much space they take. The view returns, via +dw::core::View::getHScrollbarThickness and +dw::core::View::getVScrollbarThickness, how thick they will be, when +visible. + +Viewport sizes, which denote the size of the viewport widgets, include +scrollbar thicknesses. When referring to the viewport \em excluding +the scrollbars space, we will call it "usable viewport size", this is +the area, which is used to display the canvas. + +

Drawing

+ +A view must implement several drawing methods, which work on the whole +canvas. If it is necessary to convert them (e.g. into +dw::fltk::FltkViewport), this is done in a way fully transparent to +dw::core::Widget and dw::core::Layout, instead, this is done by the +view implementation. + +There exist following situations: + +
    +
  • A view gets an expose event: It will delegate this to the + layout (dw::core::Layout::draw), which will then pass it to the + widgets (dw::core::Widget::draw), with the view as a parameter. + Eventually, the widgets will call drawing methods of the view. + +
  • A widget requests a redraw: In this case, the widget will + delegate this to the layout (dw::core::Layout::queueDraw), which + delegates it to the view (dw::core::View::queueDraw). + Typically, the view will queue these requests for efficiency. + +
  • A widget requests a resize: This case is described below, in short, + dw::core::View::queueDrawTotal is called for the view. +
+ +If the draw method of a widget is implemented in a way that it may +draw outside of the widget's allocation, it should draw into a +clipping view. A clipping view is a view related to the actual +view, which guarantees that the parts drawn outside are discarded. At +the end, the clipping view is merged into the actual view. Sample +code: + +\code +void Foo::draw (dw::core::View *view, dw::core::Rectangle *area) +{ + // 1. Create a clipping view. + dw::core::View clipView = + view->getClippingView (allocation.x, allocation.y, + allocation.width, getHeight ()); + + // 2. Draw into clip_view + clipView->doSomeDrawing (...); + + // 3. Draw the children, they receive the clipping view as argument. + dw::core::Rectangle *childArea + for () { + if (child->intersects (area, &childArea)) + child->draw (clipView, childArea); + } + + // 4. Merge + view->mergeClippingView (clipView); +} +\endcode + +A drawing process is always embedded into calls of +dw::core::View::startDrawing and dw::core::View::finishDrawing. An +implementation of this may e.g. use backing pixmaps, to prevent +flickering. + + +

Sizes

+ +In the simplest case, the view does not have any influence on +the canvas size, so it is told about changes of the +canvas size by a call to dw::core::View::setCanvasSize. This happens +in the following situations: + +
    +
  • dw::core::Layout::addWidget, +
  • dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget), + and +
  • dw::core::Layout::queueResize (called by + dw::core::Widget::queueResize, when a widget itself requests a size + change). +
+ +

Viewports

+ +There are two cases where the viewport size changes: + +
    +
  • As an reaction on a user event, e.g. when the user changes the + window size. In this case, the view delegates this + change to the layout, by calling + dw::core::Layout::viewportSizeChanged. + +
  • The viewport size may also depend on the visibility of UI + widgets, which depend on the world size, e.g scrollbars, + generally called "viewport markers". This is described in a separate + section. +
+ +After the creation of the layout, the viewport size is undefined. When +a view is attached to a layout, and this view can already specify +its viewport size, it may call +dw::core::Layout::viewportSizeChanged within the implementation of +dw::core::Layout::setLayout. If not, it may do this as soon as the +viewport size is known. + +Generally, the scrollbars have to be considered. If e.g. an HTML page +is rather small, it looks like this: + +\image html dw-viewport-without-scrollbar.png + +If some more data is retrieved, so that the height exceeds the +viewport size, the text has to be rewrapped, since the available width +gets smaller, due to the vertical scrollbar: + +\image html dw-viewport-with-scrollbar.png + +Notice the different line breaks. + +This means circular dependencies between these different sizes: + +
    +
  1. Whether the scrollbars are visible or not, determines the + usable space of the viewport. + +
  2. From the usable space of the viewport, the size hints for the + toplevel are calculated. + +
  3. The size hints for the toplevel widgets may have an effect on its + size, which is actually the canvas size. + +
  4. The canvas size determines the visibility of the scrollbarss. +
+ +To make an implementation simpler, we simplify the model: + +
    +
  1. For the calls to dw::core::Widget::setAscent and + dw::core::Widget::setDescent, we will always exclude the + horizontal scrollbar thickness (i.e. assume the horizontal + scrollbar is used, although the visibility is determined correctly). + +
  2. For the calls to dw::core::Widget::setWidth, we will calculate + the usable viewport width, but with the general assumption, that + the widget generally gets higher. +
+ +This results in the following rules: + +
    +
  1. Send always (when it changes) dw::core::Layout::viewportHeight + minus the maximal value of dw::core::View::getHScrollbarThickness as + argument to dw::core::Widget::setAscent, and 0 as argument to + dw::core::Widget::setDescent. + +
  2. There is a flag, dw::core::Layout::canvasHeightGreater, which is set + to false in the following cases: + +
      +
    • dw::core::Layout::addWidget, +
    • dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget), + and +
    • dw::core::Layout::viewportSizeChanged. +
    + + Whenever the canvas size is calculated (dw::core::Layout::resizeIdle), + and dw::core::Layout::canvasHeightGreater is false, a test is made, + whether the widget has in the meantime grown that high, that the second + argument should be set to true (i.e. the vertical scrollbar gets visible). + As soon as and dw::core::Layout::canvasHeightGreater is true, no such test + is done anymore. +
+ +*/ diff --git a/devdoc/dw-layout-widgets.doc b/devdoc/dw-layout-widgets.doc new file mode 100644 index 00000000..e0215562 --- /dev/null +++ b/devdoc/dw-layout-widgets.doc @@ -0,0 +1,267 @@ +/** \page dw-layout-widgets Layout and Widgets + +Both, the layouting and the drawing is delegated to a tree of +widgets. A widget represents a given part of the document, e.g. a text +block, a table, or an image. Widgets may be nested, so layouting and +drawing may be delegated by one widget to its child widgets. + +Where to define the borders of a widget, whether to combine different +widgets to one, or to split one widget into multiple ones, should be +considered based on different concerns: + +
    +
  • First, there are some restrictions of Dw: + +
      +
    • The allocation (this is the space a widget allocates at + a time) of a dillo widget is always rectangular, and +
    • the allocation of a child widget must be a within the allocation + of the parent widget. +
    + +
  • Since some widgets are already rather complex, an important goal + is to keep the implementation of the widget simple. + +
  • Furthermore, the granularity should not be too fine, because of the + overhead each single widget adds. +
+ +For CSS, there will be a document tree on top of Dw, this will be +flexible enough when mapping the document structure on the widget +structure, so you should not have the document structure in mind. + +

Sizes

+ +\ref dw-widget-sizes + + +

Styles

+ +Each widget is assigned a style, see dw::core::style for more +informations. + + +

Iterators

+ +Widgets must implement dw::core::Widget::iterator. There are several +common iterators: + +
    +
  • dw::core::EmptyIterator, and +
  • dw::core::TextIterator. +
+ +Both hide the constructor, use the \em create method. + +These simple iterators only iterate through one widget, it does not +have to iterate recursively through child widgets. Instead, the type +dw::core::Content::WIDGET is returned, and the next call of +dw::core::Iterator::next will return the piece of contents \em after +(not within) this child widget. + +This makes implementation much simpler, for recursive iteration, there +is dw::core::DeepIterator. + + +

Anchors and Scrolling

+ +\todo This section is not implemented yet, after the implementation, + the documentation should be reviewed. + +Here is a description, what is to be done for a widget +implementation. How to jump to anchors, set scrolling positions +etc. is described in \ref dw-usage. + +

Anchors

+ +Anchors are position markers, which are identified by a name, which is +unique in the widget tree. The widget must care about anchors in three +different situations: + +
    +
  1. Adding an anchor is inititiated by a specific widget method, e.g. + dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be + called, + +
  2. Whenever the position of an anchor is changed, + dw::core::Widget::changeAnchor is called (typically, this is done + in the implementation of dw::core::Widget::sizeAllocateImpl). + +
  3. When a widget is destroyed, the anchor must be removed, by calling + dw::core::Widget::removeAnchor. +
+ +All these methods are delegated to dw::core::Layout, which manages +anchors centrally. If the anchor in question has been set to jump to, +the viewport position is automatically adjusted, see \ref +dw-usage. + + +

Drawing

+ +In two cases, a widget has to be drawn: + +
    +
  1. as a reaction on an expose event, +
  2. if the widget content has changed and it needs to be redrawn. +
+ +In both cases, drawing is done by the implementation of +dw::core::Widget::draw, which draws into the view. + + +Each view provides some primitive methods for drawing, most should be +obvious. Note that the views do not know anything about dillo +widgets, and so coordinates have to be passed as canvas coordinates. + +A widget may only draw in its own allocation. If this cannot be +achieved, a clipping view can be used, this is described in +\ref dw-layout-views. Generally, drawing should then look like: + +\code +void Foo::draw (dw::core::View *view, dw::core::Rectangle *area) +{ + // 1. Create a clipping view. + dw::core::View clipView = + view->getClippingView (allocation.x, allocation.y, + allocation.width, getHeight ()); + + // 2. Draw into clip_view + clipView->doSomeDrawing (...); + + // 3. Draw the children, they receive the clipping view as argument. + dw::core::Rectangle *childArea + for () { + if (child->intersects (area, &childArea)) + child->draw (clipView, childArea); + } + + // 4. Merge + view->mergeClippingView (clipView); +} +\endcode + +Clipping views are expensive, so they should be avoided when possible. + +The second argument to dw::core::Widget::draw is the region, which has +to be drawn. This may (but needs not) be used for optimization. + +If a widget contains child widgets, it must explicitly draw these +children (see also code example above). For this, there is the useful +method dw::core::Widget::intersects, which returns, which area of the +child must be drawn. + +

Explicit Redrawing

+ +If a widget changes its contents, so that it must be redrawn, it must +call dw::core::Widget::queueDrawArea or +dw::core::Widget::queueDraw. The first variant expects a region within +the widget, the second will cause the whole widget to be redrawn. This +will cause an asynchronous call of dw::core::Widget::draw. + +If only the size changes, a call to dw::core::Widget::queueResize is +sufficient, this will also queue a complete redraw (see \ref +dw-widget-sizes.) + + +

Mouse Events

+ +A widget may process mouse events. The view (\ref dw-layout-views) +passes mouse events to the layout, which then passes them to the +widgets. There are two kinds of mouse events: + +
    +
  • events returning bool, and +
  • events returning nothing (void). +
+ +The first group consists of: + +
    +
  • dw::core::Widget::buttonPressImpl, +
  • dw::core::Widget::buttonReleaseImpl, and +
  • dw::core::Widget::motionNotifyImpl. +
+ +For these events, a widget returns a boolean value, which denotes, +whether the widget has processed this event (true) or not (false). In +the latter case, the event is delegated according to the following +rules: + +
    +
  1. First, this event is passed to the bottom-most widget, in which + allocation the mouse position is in. +
  2. If the widget does not process this event (returning false), it is + passed to the parent, and so on. +
  3. The final result (whether \em any widget has processed this event) is + returned to the view. +
+ +The view may return this to the UI toolkit, which then interprets this +in a similar way (whether the viewport, a UI widget, has processed +this event). + +These events return nothing: + +
    +
  • dw::core::Widget::enterNotifyImpl and +
  • dw::core::Widget::leaveNotifyImpl. +
+ +since they are bound to a widget. + +When processing mouse events, the layout always deals with two +widgets: the widget, the mouse pointer was in, when the previous mouse +event was processed, (below called the "old widget") and the widget, +in which the mouse pointer is now ("new widget"). + +The following paths are calculated: + +
    +
  1. the path from the old widget to the nearest common ancestor of the old + and the new widget, and +
  2. the path from this ancestor to the new widget. +
+ +For the widgets along these paths, dw::core::Widget::enterNotifyImpl +and dw::core::Widget::leaveNotifyImpl are called. + +

Signals

+ +If a caller outside of the widget is interested in these events, he +can connect a dw::core::Layout::LinkReceiver. For those events with a +boolean return value, the results of the signal emission is regarded, +i.e. the delegation of an event to the parent of the widget can be +stopped by a signal receiver returning true, even if the widget method +returns false. + +First, the widget method is called, then (in any case) the signal is +emitted. + +

Selection

+ +If your widget has selectable contents, it should delegate the events +to dw::core::SelectionState (dw::core::Layout::selectionState). + + +

Miscellaneous

+ +

Cursors

+ +Each widget has a cursor, which is set by +dw::core::Widget::setCursor. If a cursor is assigned to a part of a +widget, this widget must process mouse events, and call +dw::core::Widget::setCursor explicitly. + +(This will change, cursors should become part of +dw::core::style::Style.) + +

Background

+ +Backgrounds are part of styles +(dw::core::style::Style::backgroundColor). If a widget assigns +background colors to parts of a widget (as dw::Table does for rows), +it must call dw::core::Widget::setBgColor for the children inside this +part. + +*/ diff --git a/devdoc/dw-line-breaking.doc b/devdoc/dw-line-breaking.doc new file mode 100644 index 00000000..14ab97c4 --- /dev/null +++ b/devdoc/dw-line-breaking.doc @@ -0,0 +1,470 @@ +/** \page dw-line-breaking Changes in Line-Breaking and Hyphenation + +
Info: +Should be incorporated into dw::Textblock.
+ +Introduction +============ + +For the implementation of hyphenation in dillo, not only a +hyphenation algorithm was implemented, but also, the line breaking was +changed to a simple optimization per line. Aside from the improvement +by this change per se, an important aspect is the introduction of +"penalties". Before this change, dillo put all words into a line which +fitted into it; now, a "badness" is calculated for a possible +breakpoint, and the best breakpoint, i. e. the breakpoint with the +smallest value for "badness", is chosen. This can be simply refined +to define "good" and "bad" breakpoints by assigning a "penalty"; the +best breakpoint is then the one with the smallest value of "badness + +penalty". Details can be found below. + +Example: Normal spaces have a penalty of 0, while hyphenation points +get a penalty of, say, 1, since hyphenation is generally considered as +a bit "ugly" and should rather be avoided. Consider a situation where +the word "dillo" could be hyphenated, with the following badnesses: + +- before "dillo": 0.6; +- between "dil-" and "lo": 0.2; +- after "dillo": 0.5. + +Since the penalty is added, the last value is the best one, so "dillo" +is put at the end of the line, without hyphenation. + +Under other circumstances (e. g. narrower lines), the values +might be different: + +- before "dillo": infinite; +- between "dil-" and "lo": 0.3; +- after "dillo": 1.5. + +In this case, even the addition of the penalty makes hyphenation the +best choice. + + +Literature +========== + +Breaking Paragraphs Into Lines +------------------------------ + +Although dillo does not (yet?) implement the algorithm TEX +uses for line breaking, this document shares much of the notation used +by the article *Breaking Paragraphs Into Lines* by Donald E. Knuth and +Michael F. Plass; originally published in: Software -- Practice and +Experience **11** (1981), 1119-1184; reprinted in: *Digital +Typography* by Donalt E. Knuth, CSLI Publications 1999. Anyway an +interesting reading. + +Hyphenation +----------- + +Dillo uses the algorithm by Frank Liang, which is described in his +doctoral dissertation found at http://www.tug.org/docs/liang/. There +is also a description in chapter H ("Hyphenation") of *The +TEXbook* by Donald E. Knuth, Addison-Wesley 1984. + +Pattern files can be found at +http://www.ctan.org/tex-archive/language/hyphenation. + + +Overview of Changes +=================== + +Starting with this change, dw/textblock.cc has been split up; anything +related to line breaking has been moved into +dw/textblock_linebreaking.cc. This will also be done for other aspects +like floats. (Better, however, would be a clean logical split.) + +An important change relates to the way that lines are added: before, +dillo would add a line as soon as a new word for this line was +added. Now, a line is added not before the *last* word of this line is +known. This has two important implications: + +- Some values in dw::Textblock::Line, which represented values + accumulated within the line, could be removed, since now, these + values can be calculated simply in a loop. +- On the other hand, this means that some words may not belong to any + line. For this reason, in some cases (e. g. in + dw::Textblock::sizeRequestImpl) dw::Textblock::showMissingLines is + called, which creates temporary lines, which must, under other + circumstances, be removed again by + dw::Textblock::removeTemporaryLines, since they have been created + based on limited information, and so possibly in a wrong way. (See + below for details.) + +When a word can be hyphenated, an instance of dw::Textblock::Word is +used for each part. Notice that soft hyphens are evaluated +immediately, but automatic hyphenation is done in a lazy way (details +below), so the number of instances may change. There are some new +attributes: only when dw::Textblock::Word::canBeHyphenated is set to +*true*, automatic hyphenation is allowed; it is set to false when soft +hyphens are used for a word, and (of course) by the automatic +hyphenation itself. Furthermore, dw::Textblock::Word::hyphenWidth +(more details in the comment there) has to be included when +calculating line widths. + +Some values should be configurable: dw::Textblock::HYPHEN_BREAK, the +penalty for hyphens. Also dw::Textblock::Word::stretchability, +dw::Textblock::Word::shrinkability, which are both set in +dw::Textblock::addSpace. + + +Criteria for Line-Breaking +========================== + +Before these changes to line breaking, a word (represented by +dw::Textblock::Word) had the following attributes related to +line-breaking: + +- the width of the word itself, represented by + dw::Textblock::Word::size; +- the width of the space following the word, represented by + dw::Textblock::Word::origSpace. + +In a more mathematical notation, the \f$i\f$th word has a width +\f$w_i\f$ and a space \f$s_i\f$. + +A break was possible, when there was a space between the two words, +and the first possible break was chosen. + +With hyphenation, the criteria are refined. Hyphenation should only be +used when otherwise line breaking results in very large spaces. We +define: + +- the badness \f$\beta\f$ of a line, which is greater the more the + spaces between the words differ from the ideal space; +- a penalty \f$p\f$ for any possible break point. + +The goal is to find those break points, where \f$\beta + p\f$ is +minimal. + +Examples for the penalty \f$p\f$: + +- 0 for normal line breaks (between words); +- \f$\infty\f$ to prevent a line break at all costs; +- \f$-\infty\f$ to force a line +- a positive, but finite, value for hyphenation points. + +So we need the following values: + +- \f$w_i\f$ (the width of the word \f$i\f$ itself); +- \f$s_i\f$ (the width of the space following the word \f$i\f$); +- the stretchability \f$y_i\f$, a value denoting how much the space + after word\f$i\f$ can be stretched (typically \f${1\over 2} s_i\f$ + for justified text; otherwise 0, since the spaces are not + stretched); +- the shrinkability \f$y_i\f$, a value denoting how much the space + after word\f$i\f$ can be shrunken (typically \f${1\over 3} s_i\f$ + for justified text; otherwise 0, since the spaces are not shrinked); +- the penalty \f$p_i\f$, if the line is broken after word \f$i\f$; +- a width \f$h_i\f$, which is added, when the line is broken after + word \f$i\f$. + +\f$h_i\f$ is the width of the hyphen, if the word \f$i\f$ is a part of +the hyphenated word (except the last part); otherwise 0. + +Let \f$l\f$ be the (ideal) width (length) of the line, which is +e. at the top given by the browser window width. Furthermore, all words +from \f$a\f$ to \f$b\f$ are added to the line. \f$a\f$ is fixed: we do +not modify the previous lines anymore; but our task is to find a +suitable \f$b\f$. + +We define: + +\f[W_a^b = \sum_{i=a}^{b} w_i + \sum_{i=a}^{b-1} s_i + h_b\f] + +\f[Y_a^b = {Y_0}_a^b + \sum_{i=a}^{b-1} y_i\f] + +\f[Z_a^b = {Z_0}_a^b + \sum_{i=a}^{b-1} z_i\f] + + +\f$W_a^b\f$ is the total width, \f$Y_a^b\f$ the total stretchability, +and \f$Z_a^b\f$ the total shrinkability. \f${Y_0}_a^b\f$ and +\f${Z_0}_a^b\f$ are the stretchability and shrinkability defined per +line, and applied at the borders; they are 0 for justified text, but +\f${Y_0}_a^b\f$ has a positive value otherwise, see below for details. + +Furthermore the *adjustment ratio* \f$r_a^b\f$: + +- in the ideal case that \f$W_a^b = l\f$: \f$r_a^b = 0\f$; +- if \f$W_a^b < l\f$: \f$r_a^b = (l - W_a^b) / Y_a^b\f$ + (\f$r_a^b < 0\f$ in this case); +- if \f$W_a^b > l\f$: \f$r_a^b = (l - W_a^b) / Z_a^b\f$ + (\f$r_a^b < 0\f$ in this case). + +The badness \f$\beta_a^b\f$ is defined as follows: + +- if \f$r_a^b\f$ is undefined or \f$r_a^b < -1\f$: \f$\beta_a^b = \infty\f$; +- otherwise: \f$\beta_a^b = |r_a^b|^3\f$ + +The goal is to find the value of \f$b\f$ where \f$\beta_a^b + p_b\f$ +is minimal. (\f$a\f$ is given, since we do not modify the previous +lines.) + +After a couple of words, it is not predictable whether this minimum +has already been reached. There are two cases where this is possible +for a given \f$b'\f$: + +- \f$\beta_{b'}^a = \infty\f$ (line gets too tight): + \f$a \le b < b'\f$, the minimum has to be searched between these two + values; +- \f$p_{b'} = -\infty\f$ (forced line break): + \f$a \le b \le b'\f$ (there may be another minimum of + \f$\beta_a^b\f$ before; note the \f$\le\f$ instead of \f$<\f$). + +This leads to a problem that the last words of a text block are not +displayed this way, since they do not fulfill these rules for being +added to a line. For this reason, there are "temporary" lines already +described above. + +(Note that the actual calculation differs from this description, since +integer arithmetic is used for performance, which make the actual +code more complicated. See dw::Textblock::BadnessAndPenalty for +details.) + +Ragged Borders +-------------- + +For other than justified text (left-, right-aligned and centered), the +spaces between the words are not shrinked or stretched (so \f$y_i\f$ +and \f$z_i\f$ are 0), but additional space is added to the left or +right border or to both. For this reason, an additional stretchability +\f${Y_0}_a^b\f$ is added (see definition above). Since this space at +the border is 0 in an ideal case (\f$W_a^b = l\f$), it cannot be +shrunken, so \f${Z_0}_a^b\f$ is 0. + +This is not equivalent to the calculation of the total stretchability +as done for justified text, since in this case, the stretchability +depends on the number of words: consider the typical case that all +spaces and stretchabilities are equal (\f$y_a = y_{a + 1} = \ldots = +y_b\f$). With \f$n\f$ words, the total strechability would be \f$n +\cdot y_a\f$, so increase with an increasing number of words +(\f$y_a\f$ is constant). This is correct for justified text, but for +other alignments, where only one space (or two, for centered text) is +changed, this would mean that a line with many narrow words is more +stretchable than a line with few wide words. + +It is obvious that left-aligned text can be handled in the same way as +right-aligned text. [... Centered text? ...] + +The default value for the stretchability is the line height without +the space between the lines (more precisely: the maximum of all word +heights). The exact value not so important when comparing different +possible values for the badness \f$\beta_a^b\f$, when \f${Y_0}_a^b\f$ +is nearly constant for different \f$b\f$ (which is the case for the +actual value), but it is important for the comparison with penalties, +which are constant. To be considered is also that for non-justified +text, hyphenation is differently (less) desirable; this effect can be +achieved by enlarging the stretchability, which will lead to a smaller +badness, and so make hyphenation less likely. The user can configure +the stretchability by changing the preference value +*stretchability_factor* (default: 1.0). + +(Comparison to TEX: Knuth and Plass describe a method for +ragged borders, which is effectively the same as described here (Knuth +1999, pp. 93--94). The value for the stretchability of the line +is slightly less, 1 em (ibid., see also p. 72 for the +definition of the units). However, this article suggests a value for +the hyphenation penalty, which is ten times larger than the value for +justified text; this would suggest a larger value for +*stretchability_factor*.) + + +Hyphens +======= + +Words (instances of dw::Textblock::Word), which are actually part of a +hyphenated word, are always drawn as a whole, not seperately. This +way, the underlying platform is able to apply kerning, ligatures, etc. + +Calculating the width of such words causes some problems, since it is +not required that the width of text "AB" is identical to the width of +"A" plus the width of "B", just for the reasons mentioned above. It +gets even a bit more complicated, since it is required that a word +part (instance of dw::Textblock::Word) has always the same length, +independent of whether hyphenation is applied or not. Furthermore, the +hyphen length is fixed for a word; for practical reasons, it is always +the width of a hyphen, in the given font. + +For calculating the widths, consider a word of four syllables: +A-B-C-D. There are 3 hyphenation points, and so 23 = 8 +possible ways of hyphenation: ABCD, ABC-D, AB-CD, AB-C-D, A-BCD, +A-BC-D, A-B-CD, A-B-C-D. (Some of them, like the last one, are only +probable for very narrow lines.) + +Let w(A), w(B), w(C), w(D) be the word widths (part of +dw::Textblock::Word::size), which have to be calculated, and l be a +shorthand for dw::core::Platform::textWidth. Without considering +this problem, the calculation would be simple: w(A) = l(A) +etc. However, it gets a bit more complicated. Since all +non-hyphenations are drawn as a whole, the following conditions can be +concluded: + +- from drawing "ABCD" (not hyphenated at all): w(A) + w(B) + w(C) + + w(D) = l(ABCD); +- from drawing "BCD", when hyphenated as "A-BCD" ("A-" is not + considered here): w(B) + w(C) + w(D) = l(BCD); +- likewise, from drawing "CD" (cases "AB-CD" and "A-B-CD"): w(C) + + w(D) = l(CD); +- finally, for the cases "ABC-D", "AB-C-D", "A-BC-D", and "A-B-C-D": + w(D) = l(D). + +So, the calculation is simple: + +- w(D) = l(D) +- w(C) = l(CD) - w(D) +- w(B) = l(BCD) - (w(C) + w(D)) +- w(A) = l(ABCD) - (w(B) + w(C) + w(D)) + +For calculation the hyphen widths, the exact conditions would be +over-determined, even when the possibility for individual hyphen +widths (instead of simply the text width of a hyphen character) would +be used. However, a simple approach of fixed hyphen widths will have +near-perfect results, so this is kept simple. + + +Automatic Hyphenation +===================== + +When soft hyphens are used, words are immediately divided into +different parts, and so different instances of +dw::Textblock::Word. Automatic hyphenation (using Liang's algorithm) +is, however, not applied always, but only when possibly needed, after +calculating a line without hyphenation: + +- When the line is tight, the last word of the line is hyphenated; + possibly this will result in a line with less parts of this word, + and so a less tight line. +- When the line is loose, and there is another word (for the next + line) available, this word is hyphenated; possibly, some parts of + this word are taken into this line, making it less loose. + +After this, the line is re-calculated. + +A problem arrises when the textblock is rewrapped, e. g. when the +user changes the window width. In this case, some new instances of +dw::Textblock::Word must be inserted into the word list, +dw::Textblock::words. This word list is implemented as an array, which +is dynamically increased; a simple approach would involve moving all +of the n elements after position i, so +n - i steps are necessary. This would not be a +problem, since O(n) steps are necessary; however, this will be +necessary again for the next hyphenated word (at the end of a +following line), and so on, so that +(n - i1) + +(n - i2) + ..., with +i1 < i2 < ..., +which results in O(n2) steps. For this reason, the word +list is managed by the class lout::misc::NotSoSimpleVector, which uses +a trick (a second array) to deal with exactly this problem. See there +for more details. + + +Tests +===== + +There are test HTML files in the test directory. Also, there is +a program testing automatic hyphenation, test/liang, which can +be easily extended. + + +Bugs and Things Needing Improvement +=================================== + +High Priority +------------- + +None. + +Medium Priority +--------------- + +None. + +Low Priority +------------ + +**Mark the end of a paragraph:** Should dw::core::Content::BREAK still +be used? Currently, this is redundant to +dw::Textblock::BadnessAndPenalty. + +Solved (Must Be Documented) +--------------------------- + +These have been solved recently and should be documented above. + +*Bugs in hyphenation:* There seem to be problems when breaking words +containing hyphens already. Example: "Abtei-Stadt", which is divided +into "Abtei-" and "Stadt", resulting possibly in +"Abtei--[new line]Stadt". See also below under +"Medium Priority", on how to deal with hyphens and dashes. + +**Solution:** See next. + +*Break hyphens and dashes:* The following rules seem to be relevant: + +- In English, an em-dash is used with no spaces around. Breaking + before and after the dash should be possible, perhaps with a + penalty > 0. (In German, an en-dash (Halbgeviert) with spaces around + is used instead.) +- After a hyphen, which is part of a compound word, a break should be + possible. As described above ("Abtei-Stadt"), this collides with + hyphenation. + +Where to implement? In the same dynamic, lazy way like hyphenation? As +part of hyphenation? + +Notice that Liang's algorithm may behave different regarding hyphens: +"Abtei-Stadt" is (using the patterns from CTAN) divided into "Abtei-" +and "Stadt", but "Nordrhein-Westfalen" is divided into "Nord", +"rhein-West", "fa", "len": the part containing the hyphen +("rhein-West") is untouched. (Sorry for the German words; if you have +got English examples, send them me.) + +**Solution for both:** This has been implemented in +dw::Textblock::addText, in a similar way to soft hyphens. Liang's +algorithm now only operates on the parts: "Abtei" and "Stadt"; +"Nordrhein" and "Westfalen". + +*Hyphens in adjacent lines:* It should be simple to assign a larger +penalty for hyphens, when the line before is already hyphenated. This +way, hyphens in adjacent lines are penalized further. + +**Solved:** There are always two penalties. Must be documented in +detail. + +*Incorrect calculation of extremes:* The minimal width of a text block +(as part of the width extremes, which are mainly used for tables) is +defined by everything between two possible breaks. A possible break +may also be a hyphenation point; however, hyphenation points are +calculated in a lazy way, when the lines are broken, and not when +extremes are calculated. So, it is a matter of chance whether the +calculation of the minimal width will take the two parts "dil-" and +"lo" into account (when "dillo" has already been hyphenated), or only +one part, "dillo" (when "dillo" has not yet been hyphenated), +resulting possibly in a different value for the minimal width. + +Possible strategies to deal with this problem: + +- Ignore. The implications should be minimal. +- Any solution will make it neccessary to hyphenate at least some + words when calculating extremes. Since the minimal widths of all + words are used to calculate the minimal width of the text block, the + simplest approach will hyphenate all words. This would, of course, + eliminate the performance gains of the current lazy approach. +- The latter approach could be optimized in some ways. Examples: (i) + If a word is already narrower than the current accumulated value for + the minimal width, it makes no sense to hyphenate it. (ii) In other + cases, heuristics may be used to estimate the number of syllables, + the width of the widest of them etc. + +**Solved:** Hyphenated parts of a word are not considered anymore for +width extremes, but only whole words. This is also one reason for the +introduction of the paragraphs list. + +**Also:** + +- Configuration of penalties. + +*/ diff --git a/devdoc/dw-map.doc b/devdoc/dw-map.doc new file mode 100644 index 00000000..aebeb7da --- /dev/null +++ b/devdoc/dw-map.doc @@ -0,0 +1,59 @@ +/** \page dw-map Dillo Widget Documentation Map + +This maps includes special documentations as well as longer comments +in the sources. Arrows denote references between the documents. + +\dot +digraph G { + rankdir=LR; + node [shape=record, fontname=Helvetica, fontsize=8]; + fontname=Helvetica; fontsize=8; + + dw_overview [label="Dillo Widget Overview", URL="\ref dw-overview"]; + dw_usage [label="Dillo Widget Usage", URL="\ref dw-usage"]; + dw_layout_views [label="Layout and Views", URL="\ref dw-layout-views"]; + dw_layout_widgets [label="Layout and Widgets", + URL="\ref dw-layout-widgets"]; + dw_widget_sizes [label="Sizes of Dillo Widgets", + URL="\ref dw-widget-sizes"]; + dw_changes [label="Changes to the GTK+-based Release Version", + URL="\ref dw-changes"]; + dw_images_and_backgrounds [label="Images and Backgrounds in Dw", + URL="\ref dw-images-and-backgrounds"]; + dw_Image [label="dw::Image", URL="\ref dw::Image"]; + dw_core_Imgbuf [label="dw::core::Imgbuf", URL="\ref dw::core::Imgbuf"]; + dw_core_SelectionState [label="dw::core::SelectionState", + URL="\ref dw::core::SelectionState"]; + dw_core_style [label="dw::core::style", URL="\ref dw::core::style"]; + dw_Table [label="dw::Table", URL="\ref dw::Table"]; + dw_Textblock [label="dw::Textblock", URL="\ref dw::Textblock"]; + dw_core_ui [label="dw::core::ui", URL="\ref dw::core::ui"]; + + dw_overview -> dw_changes; + dw_overview -> dw_usage; + dw_overview -> dw_core_style; + dw_overview -> dw_core_ui; + dw_overview -> dw_images_and_backgrounds; + dw_overview -> dw_layout_widgets; + dw_overview -> dw_widget_sizes; + dw_overview -> dw_layout_views; + + dw_usage -> dw_Table; + dw_usage -> dw_Textblock; + dw_usage -> dw_core_style; + dw_usage -> dw_core_ui; + dw_usage -> dw_images_and_backgrounds; + + dw_layout_widgets -> dw_widget_sizes; + dw_layout_widgets -> dw_core_SelectionState; + + dw_widget_sizes -> dw_Table; + dw_widget_sizes -> dw_Textblock; + + dw_images_and_backgrounds -> dw_core_Imgbuf; + dw_images_and_backgrounds -> dw_Image; + + dw_core_style -> dw_Textblock; +} +\enddot +*/ \ No newline at end of file diff --git a/devdoc/dw-out-of-flow-2.doc b/devdoc/dw-out-of-flow-2.doc new file mode 100644 index 00000000..d9d70565 --- /dev/null +++ b/devdoc/dw-out-of-flow-2.doc @@ -0,0 +1,69 @@ +/** \page dw-out-of-flow-2 Handling Elements Out Of Flow (notes 2) + +This has to be integrated into \ref dw-out-of-flow. + +Constructing a page with floats +------------------------------- +When a page is constructed (dw::Textblock::addWord), the *generating* +block tells the positions of floats (or, generally, widgets out of +flow) via dw::OutOfFlowMgr::tellPosition. This method considers +already collisions with other floats (only previous floats; floats +following this float are not considered); after the call, +dw::OutOfFlowMgr::getBorder will return correct values. + +dw::OutOfFlowMgr::tellPosition also checks for overlaps of this float +with other textblocks, except this textblock (the *generator*, which +is just constructed, so nothing has to be done). The fact that the +position of the float is the top, and so the float has only an +allocation below this position, leads to the effect that only the +textblocks following the generator are affected. (**Check:** Can the +search be limited here?) When a page is constructed, no textblocks +should be following the generating block, so no textblocks are +affected. + +**Todo:** Clarify details of line breaking (\ref dw-line-breaking). + +Float changes its size +---------------------- +The float itself will call queueResize, which will result in a call of +markSizeChange for the *containing* block, which will then call +dw::OutOfFlowMgr::markSizeChange. Here, the vloat is only *marked* as +dirty; the size will be calculated later (in +dw::OutOfFlowMgr::ensureFloatSize). + +This will trigger the resize idle function, so sizeRequest and +sizeAllocate for all floats and textblocks. In this run, +dw::OutOfFlowMgr::hasRelationChanged will return *true*, and so result +in a call of dw::Textblock::borderChanged, and trigger a second run of +the resize idle function, dealing correctly with the new size. + +(This case is handles in a not perfectly optimal way, since two runs +of the resize idle function are neccessary; but size changes of floats +is not a very common case. + +When a page is constructed (see above), a changing size of a float +currently constructed typically only affects the most bottom +textblock; the other textblocks are not covered by this float.) + +**Error:** In this case, new collisions are not yet considered. + + +Changing the width of the page +------------------------------ + +When the page width is changed, this will result in a reconstruction +of the page; see *Constructing a page with floats*. Anyway, checking +for overlaps will play a more important role. This is handled in an +optimal way by dw::OutOfFlowMgr::hasRelationChanged. + +**Check:** Are "cascades" avoided, like this: + +1. All textblocks are constructed. A float in textblock 1 overlaps + with textblock 2, so dw::Textblock::borderChanged is called for + textblock 2. +2. In another resize idle run, textblock 2 is constructed again. A + float in textblock 2 overlaps with textblock 3, so that + dw::Textblock::borderChanged is called for textblock 3. +3. Etc. + +*/ \ No newline at end of file diff --git a/devdoc/dw-out-of-flow-floats.doc b/devdoc/dw-out-of-flow-floats.doc new file mode 100644 index 00000000..53c6b220 --- /dev/null +++ b/devdoc/dw-out-of-flow-floats.doc @@ -0,0 +1,121 @@ +/** \page dw-out-of-flow-floats Handling Elements Out Of Flow: Floats + +(Note: Bases on work at , I plan +to split the documentation on elements out of flow into different +parts: general part, floats, positioned elements. In this document, +informations about floats are collected.) + + +GB lists and CB lists +===================== + +Floats generated by a block which is not yet allocated are initially +put into a list related to the *generator*: + +- dw::OutOfFlowMgr::TBInfo::leftFloatsGB or +- dw::OutOfFlowMgr::TBInfo::rightFloatsGB. + +These lists are also called GB lists. + +Floats of allocated generators are put into lists related to the +*container* (called CB lists): + +- dw::OutOfFlowMgr::leftFloatsCB or +- dw::OutOfFlowMgr::rightFloatsCB. + +As soon as the container is allocated, all floats are moved from the +GB lists to the CB lists (dw::OutOfFlowMgr::sizeAllocateStart → +dw::OutOfFlowMgr::moveFromGBToCB). + +Here, it is important to preserve the *generation order* (for reasons, +see below: *Sorting floats*), i. e. the order in which floats have +been added (dw::OutOfFlowMgr::addWidgetOOF). This may become a bit +more complicated in a case like this: + + + + + +
+
float 1
+
+
float 2
+
+
float 3
+
+ + +The floats are generated in this order: + +- \#fl-1 (generated by \#bl-1), +- \#fl-2 (generated by \#bl-2), +- \#fl-3 (generated by \#bl-1). + +Since the floats must be moved into the CB list in this order, it +becomes clear that the floats from one GB list cannot be moved at +once. For this reason, each float is assigned a "mark", which is +different from the last one as soon as the generator is *before* the +generator of the float added before. In the example above, there are +three generators: body, \#bl-1, and \#bl-2 (in this order), and floats +are assigned these marks: + +- \#fl-1: 0, +- \#fl-2: also 0, +- \#fl-3 is assigned 1, since its generator (\#bl-1) lies before the + last generator (\#bl-2). + +dw::OutOfFlowMgr::moveFromGBToCB will then iterate over all marks, so +that the generation order is preserved. + + +Sorting floats +============== + +Floats are sorted, to make binary search possible, in these lists: + +- for each generator: dw::OutOfFlowMgr::TBInfo::leftFloatsGB and + dw::OutOfFlowMgr::TBInfo::rightFloatsGB; +- for the container: dw::OutOfFlowMgr::leftFloatsCB and + dw::OutOfFlowMgr::rightFloatsCB. + +The other two lists, dw::OutOfFlowMgr::leftFloatsAll and +dw::OutOfFlowMgr::rightFloatsAll are not sorted at all. + +New floats are always added to the end of either list; this order is +called *generation order*. See also above: *GB lists and CB lists*. + +On the other hand, there are different sorting criteria, implemented +by different comparators, so that different kinds of keys may be used +for searching. These sorting criteria are equivalent to the generation +order. + +dw::OutOfFlowMgr::Float::CompareSideSpanningIndex compares +*sideSpanningIndex* (used to compare floats to those on the respective +other side); if you look at the definition +(dw::OutOfFlowMgr::addWidgetOOF) it becomes clear that this order is +equivalent to the generation order. + +dw::OutOfFlowMgr::Float::CompareGBAndExtIndex compares *externalIndex* +for floats with same generators, otherwise: (i) if one generator (T1) +is a direct anchestor of the other generator (T2), the child of T1, +which is an anchestor of, or identical to, T2 is compared to the float +generated by T1, using *externalIndex*, as in this example: + + T1 -+-> child --> ... -> T2 -> Float + `-> Float + +Otherwise, the two blocks are compared, according to their position in +dw::OutOfFlowMgr::tbInfos: + + common anchestor -+-> ... --> T1 -> Float + `-> ... --> T2 -> Float + +This is equivalent to the generation order, as long it is ensured that +*externalIndex* reflects the generation order within a generating +block, for both floats and child blocks. + +dw::OutOfFlowMgr::Float::ComparePosition ... + +*/ diff --git a/devdoc/dw-out-of-flow.doc b/devdoc/dw-out-of-flow.doc new file mode 100644 index 00000000..ea4a52bc --- /dev/null +++ b/devdoc/dw-out-of-flow.doc @@ -0,0 +1,214 @@ +/** \page dw-out-of-flow Handling Elements Out Of Flow + + +
Info: +Should be incorporated into dw::Textblock.
+ +Introduction +============ + +This texts deals with both floats and absolute positions, which have +in common that there is a distinction between generating block and +containing block (we are here using the same notation as in the +CSS 2 specification). Consider this snippet (regarding floats): + + +
    +
  • Some text.
  • +
  • +
    Some longer text, so + that the effect described in this passage can be + demonstrated. +
    + Some more and longer text.
  • +
  • Final text. Plus some more to demonstrate how text flows + around the float on the right side.
  • +
+ +which may be rendered like this + +\image html dw-floats-01.png + +The float (the DIV section, yellow in the image) is defined +("generated") within the list item (blue), so, in CSS 2 terms, the +list item is the generating block of the float. However, as the image +shows, the float is not contained by the list item, but another block, +several levels above (not shown here). In terms of ::dw, this means +that the dw::Textblock representing the float cannot be a child of the +dw::Textblock representing the generating block, the list item, since +the allocation of a child widget must be within the allocation of the +parent widget. Instead, to each dw::Textblock, another dw::Textblock +is assigned as the containing box. + +(Notice also that other text blocks must regard floats to calculate +their borders, and so their size. In this example, the following list +item (green) must consider the position of the float. This is +discussed in detail in the next section.) + +Both in this text and the code, generating and containing block are +abbreviated with **GB** and **CB**, respectively. + + +Implementation overview +======================= + +Widget level +------------ +The terms *generating block* and *containing block* have been raised +to a higher level, the one of dw::core::Widget, and are here called +*generating widget* and *containing widget*. To represent the +distinction, the type of dw::core::Content has been split into three +parts: + +- If a widget is out of flow, the generating widget keeps a reference + with the type dw::core::Content::WIDGET_OOF_REF, while the + containing block refers to it as dw::core::Content::WIDGET_OOF_CONT. +- For widgets within flow, dw::core::Content::WIDGET_IN_FLOW is used. + +Notice that in the first case, there are two pieces of content +referring to the same widget. + +An application of this distinction is iterators. [TODO: more. And +still missing: DeepIterator may need the generating parent widget in +some cases.] + + +Textblock level +--------------- +Both dw::Textblock::notifySetAsTopLevel and +dw::Textblock::notifySetParent set the member +dw::Textblock::containingBlock appropriately, (according to rules +which should be defined in this document). + +Handling widgets out of flow is partly the task of the new class +dw::OutOfFlowMgr, which is stored by dw::Textblock::outOfFlowMgr, but +only for containing blocks. Generating blocks should refer to +*containingBlock->outOfFlowMgr*. (Perhaps dw::OutOfFlowMgr may become +independent of dw::Textblock.) + +dw::Textblock::addWidget is extended, so that floats and absolutely +positioned elements can be added. Notice that not *this* widget, but +the containing block becomes the parent of the newly added child, if +it is out of flow. dw::Textblock::addWidget decides this by calling +dw::OutOfFlowMgr::isOutOfFlow. (See new content types above.) + +dw::core::Widget::parentRef has become a new representation. Before, +it represented the line numer. Now (least signifant bit left): + + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + | line number | 0 | + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + | left float index | 0 | 0 | 1 | + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + | right float index | 1 | 0 | 1 | + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + | absolutely positioned index | 1 | 1 | + +---+ - - - +---+---+- - - - - -+---+---+---+---+ + +Details are hidden by static inline methods of dw::OutOfFlowMgr. + + +The sizeRequest/sizeAllocate problem +======================================== + +*See also:* \ref dw-widget-sizes, especially the section *Rules for +Methods Related to Resizing*. + +The size/position model of ::dw consists mainly of the following two +steps: + +1. First, the size of the toplevel widget is calculated. Size + calculation typically depends on the sizes of the widgets, which + are calculated recursively, but not more. +2. After this, the toplevel widget is allocated at position (0, 0), + with the previosly calculated size. Each widget must allocate its + children; here, the condition for the toplevel widget (allocated + size equals requested size) is not necessary; instead, each widget + may be allocated at every size. + +Especially for floats, this model becomes a bit difficult, for reasons +described below. For the solutions, much is centralized at the level +of the containing block, which delegates most to an instance of +dw::OutOfFlowMgr (details below). + +**The size of a widget depends on the size not only of the children.** +In the example above, the last list item (green, following the +generating list item) must know the size of the the float (which is +not a child or, generally, descendant) to determine the borders, which +is done in dw::Textblock::sizeRequestImpl. + +For this, the size model has been extended (see \ref dw-widget-sizes, +section *Rules for Methods Related to Resizing*): *sizeRequest* can be +called within *sizeRequestImpl* for other widgets that children (with +some caution). Namely, dw::Textblock::sizeRequestImpl calls +dw::core::Widget::sizeRequest for the float, via +dw::OutOfFlowMgr::getBorder and dw::OutOfFlowMgr::ensureFloatSize. + +**The size of a widget depends on the allocation of another widget.** +In the example above, both list items (blue and green) must know the +position of the float widget, within dw::Textblock::sizeRequestImpl, +to calculate the borders. The position, however, is stored in the +allocation, which is typically calculated later. + +Here, two cases must be distinguished. The position of a float is +always **relative to its generating block**, so for calculating the +borders for the generating block, the allocation needs not to be +know. For other textblocks, it needs to be known, so the calculation +of the borders will ignore floats generated by other textblocks, until +all widgets are allocated. The latter will call (when neccessary) +dw::core::Widget::queueResize, so that all border calculations are +repeated. See below (*hasRelationChanged*) for details. + +Generally, this pattern (distinguishing between GB and CB) can be +found everywhere in dw::OutOfFlowMgr. + +For details see: + +- dw::OutOfFlowMgr::getLeftBorder, dw::OutOfFlowMgr::getRightBorder, + dw::OutOfFlowMgr::getBorder (called by the first two), and + especially, dw::OutOfFlowMgr::getFloatsListForTextblock (called by + the latter), where these three cases are distinguished; +- dw::OutOfFlowMgr::sizeAllocateStart, + dw::OutOfFlowMgr::sizeAllocateEnd which are called by the containing + block. + +(This could be solved in a more simple, elegant way, when +*sizeRequest* would depend on the position. This is, however, only a +vague idea, perhaps not even feasible, and for which there are no +concrete plans, certainly not in \ref dw-grows.) + + +Implementation details +====================== + +- CB and GB lists (general pattern) (see previous section) +- binary search; different search criteria, how they accord +- lastLeftTBIndex, lastRightTBIndex etc. +- limitiation of search; extIndex etc. + + +How *hasRelationChanged* works +============================== + +... + + +Integration of line breaking and floats +======================================= + +(Positioning of floats, loop, recent works.) + + +Absolute and fixed positiones +============================= + +See . + +*/ \ No newline at end of file diff --git a/devdoc/dw-overview.doc b/devdoc/dw-overview.doc new file mode 100644 index 00000000..0c4ffb53 --- /dev/null +++ b/devdoc/dw-overview.doc @@ -0,0 +1,158 @@ +/** \page dw-overview Dillo Widget Overview + +Note: If you are already familiar with the Gtk+-based version of Dw, +read \ref dw-changes. + + +The module Dw (Dillo Widget) is responsible for the low-level rendering of +all resources, e.g. images, plain text, and HTML pages (this is the +most complex type). Dw is \em not responsible for parsing HTML, or +decoding image data. Furthermore, the document tree, which is planned +for CSS, is neither a part of Dw, instead, it is a new module on top +of Dw. + +The rendering, as done by Dw, is split into two phases: + +
    +
  • the \em layouting, this means calculating the exact positions of + words, lines, etc. (in pixel position), and +
  • the \em drawing, i.e. making the result of the layouting visible + on the screen. +
+ +The result of the layouting allocates an area, which is called +\em canvas. + +

Structure

+ +The whole Dw module can be split into the following parts: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="open", fontname=Helvetica, fontsize=10, + labelfontname=Helvetica, labelfontsize=10, + color="#404040", labelfontcolor="#000080"]; + + subgraph cluster_core { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + label="Platform independent core"; + + Layout [URL="\ref dw::core::Layout"]; + Platform [URL="\ref dw::core::Platform", color="#ff8080"]; + View [URL="\ref dw::core::View", color="#ff8080"]; + Widget [URL="\ref dw::core::Widget", color="#a0a0a0"]; + } + + subgraph cluster_fltk { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + label="FLTK specific part (as an\nexample for the platform specific\n\ +implementations)"; + + subgraph cluster_fltkcore { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + label="FLTK core"; + + FltkPlatform [URL="\ref dw::fltk::FltkPlatform"]; + FltkView [URL="\ref dw::fltk::FltkView", color="#ff8080"]; + } + + FltkViewport [URL="\ref dw::fltk::FltkViewport"]; + FltkPreview [URL="\ref dw::fltk::FltkPreview"]; + } + + subgraph cluster_widgets { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + label="Platform independent widgets"; + + Textblock [URL="\ref dw::Textblock"]; + AlignedTextblock [URL="\ref dw::AlignedTextblock", color="#a0a0a0"]; + Table [URL="\ref dw::Table"]; + Image [URL="\ref dw::Image"]; + etc1 [label="..."]; + etc2 [label="..."]; + } + + Layout -> Platform [headlabel="1", taillabel="1"]; + Layout -> View [headlabel="*", taillabel="1"]; + + Layout -> Widget [headlabel="1", taillabel="1", label="topLevel"]; + Widget -> Widget [headlabel="*", taillabel="1", label="children"]; + + Widget -> Textblock [arrowhead="none", arrowtail="empty", dir="both"]; + Widget -> Table [arrowhead="none", arrowtail="empty", dir="both"]; + Widget -> Image [arrowhead="none", arrowtail="empty", dir="both"]; + Widget -> etc1 [arrowhead="none", arrowtail="empty", dir="both"]; + Textblock -> AlignedTextblock [arrowhead="none", arrowtail="empty", + dir="both"]; + AlignedTextblock -> etc2 [arrowhead="none", arrowtail="empty", dir="both"]; + + Platform -> FltkPlatform [arrowhead="none", arrowtail="empty", dir="both", + style="dashed"]; + FltkPlatform -> FltkView [headlabel="*", taillabel="1"]; + + View -> FltkView [arrowhead="none", arrowtail="empty", dir="both"]; + FltkView -> FltkViewport [arrowhead="none", arrowtail="empty", dir="both", + style="dashed"]; + FltkView -> FltkPreview [arrowhead="none", arrowtail="empty", dir="both", + style="dashed"]; +} +\enddot + +
[\ref uml-legend "legend"]
+ +\em Platform means in most cases the underlying UI toolkit +(e.g. FLTK). A layout is bound to a specific platform, but multiple +platforms may be handled in one program. + +A short overview: + +
    +
  • dw::core::Layout is the central class, it manages the widgets and the + view, and provides delegation methods for the platform. + +
  • The layouting is done by a tree of widgets (details are described in + \ref dw-layout-widgets), also the drawing, which is finally delegated + to the view. + +
  • The view (implementation of dw::core::View) provides primitive methods + for drawing, but also have an influence on + the canvas size (via size hints). See \ref dw-layout-views for details. + +
  • Some platform dependencies are handled by implementations + of dw::core::Platform. +
+ + +

Header Files

+ +The structures mentioned above can be found in the following header +files: + +
    +
  • Anything from the Dw core in core.hh. Do not include the single files. + +
  • The single widgets can be found in the respective header files, e.g. + image.hh for dw::Image. + +
  • The core of the FLTK implementation is defined in fltkcore.hh. This + includes dw::fltk::FltkPlatform, dw::fltk::FltkView, but not the concrete + view implementations. + +
  • The views can be found in single header files, e.g fltkviewport.hh for + dw::fltk::FltkViewport. +
+ + +

Further Documentations

+ +A complete map can be found at \ref dw-map. + +
    +
  • For learning, how to use Dw, read \ref dw-usage and related documents, + dw::core::style, dw::core::ui and \ref dw-images-and-backgrounds. +
  • Advanced topics are described in \ref dw-layout-widgets, + \ref dw-widget-sizes and \ref dw-layout-views. +
+ +*/ diff --git a/devdoc/dw-size-of-widget.png b/devdoc/dw-size-of-widget.png new file mode 100644 index 00000000..dbdbe0c4 Binary files /dev/null and b/devdoc/dw-size-of-widget.png differ diff --git a/devdoc/dw-style-box-model.png b/devdoc/dw-style-box-model.png new file mode 100644 index 00000000..bf2fb1f1 Binary files /dev/null and b/devdoc/dw-style-box-model.png differ diff --git a/devdoc/dw-style-length-absolute.png b/devdoc/dw-style-length-absolute.png new file mode 100644 index 00000000..9ea28cad Binary files /dev/null and b/devdoc/dw-style-length-absolute.png differ diff --git a/devdoc/dw-style-length-percentage.png b/devdoc/dw-style-length-percentage.png new file mode 100644 index 00000000..b1ad79c9 Binary files /dev/null and b/devdoc/dw-style-length-percentage.png differ diff --git a/devdoc/dw-style-length-relative.png b/devdoc/dw-style-length-relative.png new file mode 100644 index 00000000..ee79b1a9 Binary files /dev/null and b/devdoc/dw-style-length-relative.png differ diff --git a/devdoc/dw-textblock-collapsing-spaces-1-1.png b/devdoc/dw-textblock-collapsing-spaces-1-1.png new file mode 100644 index 00000000..d528dfb2 Binary files /dev/null and b/devdoc/dw-textblock-collapsing-spaces-1-1.png differ diff --git a/devdoc/dw-textblock-collapsing-spaces-1-2.png b/devdoc/dw-textblock-collapsing-spaces-1-2.png new file mode 100644 index 00000000..483e79d1 Binary files /dev/null and b/devdoc/dw-textblock-collapsing-spaces-1-2.png differ diff --git a/devdoc/dw-textblock-collapsing-spaces-2-1.png b/devdoc/dw-textblock-collapsing-spaces-2-1.png new file mode 100644 index 00000000..0a03ea80 Binary files /dev/null and b/devdoc/dw-textblock-collapsing-spaces-2-1.png differ diff --git a/devdoc/dw-textblock-collapsing-spaces-2-2.png b/devdoc/dw-textblock-collapsing-spaces-2-2.png new file mode 100644 index 00000000..b89c6254 Binary files /dev/null and b/devdoc/dw-textblock-collapsing-spaces-2-2.png differ diff --git a/devdoc/dw-usage.doc b/devdoc/dw-usage.doc new file mode 100644 index 00000000..a23920b8 --- /dev/null +++ b/devdoc/dw-usage.doc @@ -0,0 +1,375 @@ +/** \page dw-usage Dillo Widget Usage + +This document describes the usage of Dw, without going too much into +detail. + + +

Getting Started

+ +In this section, a small runnable example is described, based on the +FLTK implementation. + +As described in \ref dw-overview, the following objects are needed: + +
    +
  • dw::core::Layout, +
  • an implementation of dw::core::Platform (we will use + dw::fltk::FltkPlatform), +
  • at least one implementation of dw::core::View (dw::fltk::FltkViewport), + and +
  • some widgets (for this example, only a simple dw::Textblock). +
+ +First of all, the necessary \#include's: + +\code +#include +#include + +#include "dw/core.hh" +#include "dw/fltkcore.hh" +#include "dw/fltkviewport.hh" +#include "dw/textblock.hh" +\endcode + +Everything is put into one function: + +\code +int main(int argc, char **argv) +{ +\endcode + +As the first object, the platform is instantiated: + +\code + dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform (); +\endcode + +Then, the layout is created, with the platform attached: + +\code + dw::core::Layout *layout = new dw::core::Layout (platform); +\endcode + +For the view, we first need a FLTK window: + +\code + Fl_Window *window = new Fl_Window(200, 300, "Dw Example"); + window->begin(); +\endcode + +After this, we can create a viewport, and attach it to the layout: + +\code + dw::fltk::FltkViewport *viewport = + new dw::fltk::FltkViewport (0, 0, 200, 300); + layout->attachView (viewport); +\endcode + +Each widget needs a style (dw::core::style::Style, see dw::core::style), +so we construct it here. For this, we need to fill a +dw::core::style::StyleAttrs structure with values, and call +dw::core::style::Style::create (latter is done further below): + +\code + dw::core::style::StyleAttrs styleAttrs; + styleAttrs.initValues (); + styleAttrs.margin.setVal (5); +\endcode + +dw::core::style::StyleAttrs::initValues sets several default +values. The last line sets a margin of 5 pixels. Next, we need a +font. Fonts are created in a similar way, first, the attributes are +defined: + +\code + dw::core::style::FontAttrs fontAttrs; + fontAttrs.name = "Bitstream Charter"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = dw::core::style::FONT_VARIANT_NORMAL; +\endcode + +Now, the font can be created: + +\code + styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs); +\endcode + +As the last attributes, the background and forground colors are +defined, here dw::core::style::Color::createSimple must be called: + +\code + styleAttrs.color = + dw::core::style::Color::create (layout, 0x000000); + styleAttrs.backgroundColor = + dw::core::style::Color::create (layout, 0xffffff); +\endcode + +Finally, the style for the widget is created: + +\code + dw::core::style::Style *widgetStyle = + dw::core::style::Style::create (layout, &styleAttrs); +\endcode + +Now, we create a widget, assign a style to it, and set it as the +toplevel widget of the layout: + +\code + dw::Textblock *textblock = new dw::Textblock (false); + textblock->setStyle (widgetStyle); + layout->setWidget (textblock); +\endcode + +The style is not needed anymore (a reference is added in +dw::core::Widget::setStyle), so it should be unreferred: + +\code + widgetStyle->unref(); +\endcode + +Now, some text should be added to the textblock. For this, we first +need another style. \em styleAttrs can still be used for this. We set +the margin to 0, and the background color to "transparent": + +\code + styleAttrs.margin.setVal (0); + styleAttrs.backgroundColor = NULL; + + dw::core::style::Style *wordStyle = + dw::core::style::Style::create (layout, &styleAttrs); +\endcode + +This loop adds some paragraphs: + +\code + for(int i = 1; i <= 10; i++) { + char buf[4]; + sprintf(buf, "%d.", i); + + char *words[] = { "This", "is", "the", buf, "paragraph.", + "Here", "comes", "some", "more", "text", + "to", "demonstrate", "word", "wrapping.", + NULL }; + + for(int j = 0; words[j]; j++) { + textblock->addText(strdup(words[j]), wordStyle); +\endcode + +Notice the \em strdup, dw::Textblock::addText will feel responsible +for the string, and free the text at the end. (This has been done to +avoid some overhead in the HTML parser.) + +The rest is simple, it also includes spaces (which also have styles): + +\code + textblock->addSpace(wordStyle); + } +\endcode + +Finally, a paragraph break is added, which is 10 pixels high: + +\code + textblock->addParbreak(10, wordStyle); + } +\endcode + +Again, this style should be unreferred: + +\code + wordStyle->unref(); +\endcode + +After adding text, this method should always be called (for faster +adding large text blocks): + +\code + textblock->flush (); +\endcode + +Some FLTK stuff to finally show the window: + +\code + window->resizable(viewport); + window->show(); + int errorCode = Fl::run(); +\endcode + +For cleaning up, it is sufficient to destroy the layout: + +\code + delete layout; +\endcode + +And the rest + +\code + return errorCode; +} +\endcode + +If you compile and start the program, you should see the following: + +\image html dw-example-screenshot.png + +Try to scroll, or to resize the window, you will see, that everything +is done automatically. + +Of course, creating new widgets, adding text to widgets etc. can also +be done while the program is running, i.e. after fltk::run has been +called, within timeouts, idles, I/O functions etc. Notice that Dw is +not thread safe, so that everything should be done within one thread. + +With the exception, that you have to call dw::Textblock::flush, +everything gets immediately visible, within reasonable times; Dw has +been optimized for frequent updates. + + +

List of all Widgets

+ +These widgets are used within dillo: + +
    +
  • dw::core::ui::Embed +
  • dw::AlignedTextblock +
  • dw::Bullet +
  • dw::Ruler +
  • dw::Image +
  • dw::ListItem +
  • dw::Table +
  • dw::TableCell +
  • dw::Textblock +
+ +If you want to create a new widget, refer to \ref dw-layout-widgets. + + +

List of Views

+ +There are three dw::core::View implementations for FLTK: + +
    +
  • dw::fltk::FltkViewport implements a viewport, which is used in the + example above. + +
  • dw::fltk::FltkPreview implements a preview window, together with + dw::fltk::FltkPreviewButton, it is possible to have a scaled down + overview of the whole canvas. + +
  • dw::fltk::FltkFlatView is a "flat" view, i.e. it does not support + scrolling. It is used for HTML buttons, see + dw::fltk::ui::FltkComplexButtonResource and especially + dw::fltk::ui::FltkComplexButtonResource::createNewWidget for details. +
+ +More informations about views in general can be found in \ref +dw-layout-views. + + +

Iterators

+ +For examining generally the contents of widgets, there are iterators +(dw::core::Iterator), created by the method +dw::core::Widget::iterator (see there for more details). + +These simple iterators only iterate through one widget, and return +child widgets as dw::core::Content::WIDGET. The next call of +dw::core::Iterator::next will return the piece of contents \em after +(not within) this child widget. + +If you want to iterate through the whole widget trees, there are two +possibilities: + +
    +
  1. Use a recursive function. Of course, with this approach, you are + limited by the program flow. + +
  2. Maintain a stack of iterators, so you can freely pass this stack + around. This is already implemented, as dw::core::DeepIterator. +
+ +As an example, dw::core::SelectionState represents the selected region +as two instances of dw::core::DeepIterator. + + +

Finding Text

+ +See dw::core::Layout::findtextState and dw::core::FindtextState +(details in the latter). There are delegation methods: + +
    +
  • dw::core::Layout::search and +
  • dw::core::Layout::resetSearch. +
+ + +

Anchors and Scrolling

+ +In some cases, it is necessary to scroll to a given position, or to +an anchor, programmatically. + +

Anchors

+ +Anchors are defined by widgets, e.g. dw::Textblock defines them, when +dw::Textblock::addAnchor is called. To jump to a specific anchor +within the current widget tree, use dw::core::Layout::setAnchor. + +This can be done immediately after assignig a toplevel widget, even +when the anchor has not yet been defined. The layout will remember the +anchor, and jump to the respective position, as soon as possible. Even +if the anchor position changes (e.g., when an anchor is moved +downwards, since some space is needed for an image in the text above), +the position is corrected. + +As soon as the user scrolls the viewport, this correction is not done +anymore. If in dillo, the user request a page with an anchor, which is +quite at the bottom of the page, he may be get interested in the text +at the beginning of the page, and so scrolling down. If then, after +the anchor has been read and added to the dw::Textblock, this anchor +would be jumped at, the user would become confused. + +The anchor is dismissed, too, when the toplevel widget is removed +again. + +\todo Currently, anchors only define vertical positions. + +

Scrolling

+ +To scroll to a given position, use the method +dw::core::Layout::scrollTo. It expects several parameters: + +
    +
  • a horizontal adjustment parameter, defined by dw::core::HPosition, +
  • a vertical adjustment parameter, defined by dw::core::VPosition, and +
  • a rectangle (\em x, \em y, \em width and \em heigh) of the region + to be adjusted. +
+ +If you just want to move the canvas coordinate (\em x, \em y) into the +upper left corner of the viewport, you can call: + +\code +dw::core::Layout *layout; +// ... +layout->scrollTo(dw::core::HPOS_LEFT, dw::core::VPOS_TOP, 0, 0, 0, 0); +\endcode + +By using dw::core::HPOS_NO_CHANGE or dw::core::VPOS_NO_CHANGE, you can +change only one dimension. dw::core::HPOS_INTO_VIEW and +dw::core::VPOS_INTO_VIEW will cause the viewport to move as much as +necessary, that the region is visible in the viewport (this is +e.g. used for finding text). + + +

Further Documentations

+ +
    +
  • dw::core::style +
  • dw::core::ui +
  • \ref dw-images-and-backgrounds +
+ +*/ diff --git a/devdoc/dw-viewport-with-scrollbar.png b/devdoc/dw-viewport-with-scrollbar.png new file mode 100644 index 00000000..7ac62de3 Binary files /dev/null and b/devdoc/dw-viewport-with-scrollbar.png differ diff --git a/devdoc/dw-viewport-without-scrollbar.png b/devdoc/dw-viewport-without-scrollbar.png new file mode 100644 index 00000000..8aa20fec Binary files /dev/null and b/devdoc/dw-viewport-without-scrollbar.png differ diff --git a/devdoc/dw-widget-sizes.doc b/devdoc/dw-widget-sizes.doc new file mode 100644 index 00000000..a82d3b99 --- /dev/null +++ b/devdoc/dw-widget-sizes.doc @@ -0,0 +1,277 @@ +/** \page dw-widget-sizes Sizes of Dillo Widgets + +
Info: +Not up to date, see \ref dw-grows.
+ +Allocation +========== + +Each widget has an \em allocation at a given time, this includes + +- the position (\em x, \em y) relative to the upper left corner of the + canvas, and +- the size (\em width, \em ascent, \em descent). + +The \em canvas is the whole area available for the widgets, in most +cases, only a part is seen in a viewport. The allocation of the +toplevel widget is exactly the allocation of the canvas, i.e. + +- the position of the toplevel widget is always (0, 0), and +- the canvas size is defined by the size of the toplevel widget. + +The size of a widget is not simply defined by the width and the +height, instead, widgets may have a base line, and so are vertically +divided into an ascender (which height is called \em ascent), and a +descender (which height is called \em descent). The total height is so +the sum of \em ascent and \em descent. + +Sizes of zero are allowed. The upper limit for the size of a widget is +defined by the limits of the C++ type \em int. + +\image html dw-size-of-widget.png Allocation of a Widget + +In the example in the image, the widget has the following allocation: + +- \em x = 50 +- \em y = 50 +- \em width = 150 +- \em ascent = 150 +- \em descent = 100 + +The current allocation of a widget is hold in +dw::core::Widget::allocation. It can be set from outside by +calling dw::core::Widget::sizeAllocate. This is a concrete method, +which will call dw::core::Widget::sizeAllocateImpl (see code of +dw::core::Widget::sizeAllocate for details). + +For trivial widgets (like dw::Bullet), +dw::core::Widget::sizeAllocateImpl does not need to be +implemented. For more complex widgets, the implementation should call +dw::core::Widget::sizeAllocate (not +dw::core::Widget::sizeAllocateImpl) on all child widgets, with +appropriate child allocations. dw::core::Widget::allocation should not +be changed here, this is already done in +dw::core::Widget::sizeAllocate. + + +Requisitions +============ + +A widget may prefer a given size for the allocation. This size, the +\em requisition, should be returned by the method +dw::core::Widget::sizeRequestImpl. In the simplest case, this is +independent of the context, e.g. for an +image. dw::Image::sizeRequestImpl returns the following size: + +- If no buffer has yet been assigned (see dw::Image for more details), + the size necessary for the alternative text is returned. If no + alternative text has been set, zero is returned. + +- If a buffer has been assigned (by dw::Image::setBuffer), the root + size is returned (i.e. the original size of the image to display). + +This is a bit simplified, dw::Image::sizeRequestImpl should also deal +with margins, borders and paddings, see dw::core::style. + +From the outside, dw::Image::sizeRequest should be called, which does +a bit of optimization. Notice that in dw::Image::sizeRequestImpl, no +optimization like lazy evaluation is necessary, this is already done +in dw::Image::sizeRequest. + +A widget, which has children, will likely call dw::Image::sizeRequest +on its children, to calculate the total requisition. + +The caller (this is either the dw::core::Layout, or the parent +widget), may, but also may not consider the requisition. Instead, a +widget must deal with any allocation. (For example, dw::Image scales +the image buffer when allocated at another size.) + + +Size Hints +========== + +
Info: +Size hints have been removed, see \ref dw-grows.
+ + +Width Extremes +============== + +dw::Table uses width extremes for fast calculation of column +widths. The structure dw::core::Extremes represents the minimal and +maximal width of a widget, as defined by: + +- the minimal width is the smallest width, at which a widget can still + display contents, and +- the maximal width is the largest width, above which increasing the + width- does not make any sense. + +Especially the latter is vaguely defined, here are some examples: + +- For those widgets, which do not depend on size hints, the minimal + and the maximal width is the inherent width (the one returned by + dw::core::Widget::sizeRequest). + +- For a textblock, the minimal width is the width of the widest + (unbreakable) word, the maximal width is the width of the total + paragraph (stretching a paragraph further would only waste space). + Actually, the implementation of dw::Textblock::getExtremesImpl is a + bit more complex. + +- dw::Table is an example, where the width extremes are calculated + from the width extremes of the children. + +Handling width extremes is similar to handling requisitions, a widget +must implement dw::core::Widget::getExtremesImpl, but a caller will +use dw::core::Widget::getExtremes. + + +Resizing +======== + +When the widget changes its size (requisition), it should call +dw::core::Widget::queueResize. The next call of +dw::core::Widget::sizeRequestImpl should then return the new +size. See dw::Image::setBuffer as an example. + +Interna are described in the code of dw::core::Widget::queueResize. + +

Incremental Resizing

+ +A widget may calculate its size based on size calculations already +done before. In this case, a widget must exactly know the reasons, why +a call of dw::core::Widget::sizeRequestImpl is necessary. To make use +of this, a widget must implement the following: + +1. There is a member dw::core::Widget::parentRef, which is totally + under control of the parent widget (and so sometimes not used at + all). It is necessary to define how parentRef is used by a specific + parent widget, and it has to be set to the correct value whenever + necessary. +2. The widget must implement dw::core::Widget::markSizeChange and + dw::core::Widget::markExtremesChange, these methods are called in + two cases: + 1. directly after dw::core::Widget::queueResize, with the + argument ref was passed to dw::core::Widget::queueResize, + and + 2. if a child widget has called dw::core::Widget::queueResize, + with the value of the parent_ref member of this child. + +This way, a widget can exactly keep track on size changes, and so +implement resizing in a faster way. A good example on how to use this +is dw::Textblock. + + +Rules for Methods Related to Resizing +===================================== + +Which method can be called, when the call of another method is not +finished? These rules are important in two circumstances: + +1. To know which method can be called, and, especially, which methods + *must not* be called, within the implementation of + *sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and + *markExtremesChange* (the latter two are called by *queueResize*). +2. On the other hand, to make sure that the calls, which are allowed, + are handled correctly, especially in implementations of + *sizeRequestImpl*, *markSizeChange*, *markExtremesChange* + +Generally, the rules defined below are, in case of doubt, rather +strict; when changing the rules, loosening is simpler than to tighten +them, since this will make it neccessary to review old code for calls +previously allowed but now forbidden. + +Short recap: + +- *QueueResize* directly calls *markSizeChange* and + *markExtremesChanges*, and queues an idle function for the actual + resizing (dw::core::Layout::resizeIdle). (The idle function is + called some time after *queueResize* is finished.) +- The resize idle function first calls *sizeRequest*, then + *sizeAllocate*, for the toplevel widget. + +In the following table, the rules are defined in detail. "Within call +of ..." includes all methods called from the original method: the +first row (*queueResize*) defines also the rules for +*markExtremesChanges* and *markExtremesChanges*, and in the second row +(*sizeAllocate*), even *sizeRequest* has to be considered. + +
Info: +Not up to date: *queueResize* can now be called recursively (so to +speak). See code there.
+ + + + + + + + +
Within call of ... ↓ + ... is call allowed of ... ? → + queueResize + sizeAllocate + sizeRequest + getExtremes +
queueResize + No + No1 + No1 + No1 +
sizeAllocate + Yes + Only for children2 + Yes(?) + Yes(?) +
sizeRequest + Yes3 + No + Limited4 + Limited4 +
getExtremes + Yes3 + No + Limited4 + Limited4 +
1) Otherwise, since these other methods +may be call *queueResize*, the limitation that *queueResize* must not +call *queueResize* can be violated. + +2) Could perhaps be loosened as for *sizeRequest* and +*getExtremes*, but there is probably no need. + +3) Therefore the distinction between *RESIZE_QUEUED* and +*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*, +respectively. + +4) Calls only for children are safe. In other cases, you +take a large responsibility to prevent endless recursions by +(typically indirectly) calling *sizeRequest* / *getExtremes* for +direct ancestors. +
+ +Furthermore, *sizeAllocate* can only be called within a call of +dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do +not call it outside of *sizeAllocateImpl*. The other methods can be +called outsize; e. g. *sizeRequest* is called in +dw::Textblock::addWidget. + +To avoid painful debugging, there are some tests for the cases that +one method call is strictly forbidden while another method is called. + +This could be done furthermore: + +- The tests could be refined. +- Is it possible to define exacter rules, along with a proof that no + problems (like endless recursion) can occur? + + +See also +======== + +- \ref dw-grows + +*/ diff --git a/devdoc/fltk-problems.doc b/devdoc/fltk-problems.doc new file mode 100644 index 00000000..df4f1f14 --- /dev/null +++ b/devdoc/fltk-problems.doc @@ -0,0 +1,180 @@ +/** \page fltk-problems Problems with FLTK + +

dw::fltk::FltkViewport

+ +Current problems: + +
    +
  • How should dw::fltk::FltkViewport::cancelQueueDraw be implemented? + +
  • If the value of a scrollbar is changed by the program, not the user, + the callback seems not to be called. Can this be assured? + +
  • The same for dw::fltk::FltkViewport::layout? + +
  • Also, the problems with the widgets seems to work. Also sure? + +
  • When drawing, clipping of 32 bit values is not working properly. + +
  • The item group within a selection widget (menu) should not be selectable. +
+ + +

dw::fltk::FltkPlatform

+ +
    +
  • There is the problem, that fltk::font always returns a font, the + required one, or a replacements. The latter is not wanted in all + cases, e.g. when several fonts are tested. Perhaps, this could be + solved by searching in the font list. [This was true of fltk2. + What is the state of font handling now with fltk-1.3?] + +
  • Distinction between italics and oblique would be nice + (dw::fltk::FltkFont::FltkFont). +
+ + +

dw::fltk::ui::FltkCheckButtonResource

+ +Groups of Fl_Radio_Button must be added to one Fl_Group, which is +not possible in this context. There are two alternatives: + +
    +
  1. there is a more flexible way to group radio buttons, or +
  2. radio buttons are not grouped, instead, grouping (especially + unchecking other buttons) is done by the application. +
+ +(This is mostly solved.) + +

dw::fltk::FltkImgbuf

+ +Alpha transparency should be best abstracted by FLTK itself. If not, +perhaps different implementations for different window systems could +be used. Then, it is for X necessary to use GCs with clipping masks. + + +

dw::fltk::ui::ComplexButton

+ +Unfortunately, FLTK does not provide a button with Fl_Group as parent, so +that children may be added to the button. dw::fltk::ui::ComplexButton does +exactly this, and is, in an ugly way, a modified copy of the FLTK +button. + +It would be nice, if this is merged with the standard FLTK +button. Furthermore, setting the type is strange. + +If the files do not compile, it may be useful to create a new one from +the FLTK source: + +
    +
  1. Copy Fl_Button.H from FLTK to dw/fltkcomplexbutton.hh and + src/Button.cxx to dw/fltkcomplexbutton.cc. + +
  2. In both files, rename "Button" to "ComplexButton". Automatic replacing + should work. + +
  3. Apply the changes below. +
+ +The following changes should be applied manually. + +

Changes in fltkcomplexbutton.hh

+ +First of all, the \#define's for avoiding multiple includes: + +\code +-#ifndef fltk_ComplexButton_h // fltk_Button_h formerly +-#define fltk_ComplexButton_h ++#ifndef __FLTK_COMPLEX_BUTTON_HH__ ++#define __FLTK_COMPLEX_BUTTON_HH__ +\endcode + +at the beginning and + +\code +-#endif ++#endif // __FLTK_COMPLEX_BUTTON_HH__ +\endcode + +at the end. Then, the namespace is changed: + +\code +-namespace fltk { ++namespace dw { ++namespace fltk { ++namespace ui { +\endcode + +at the beginning and + +\code +-} ++} // namespace ui ++} // namespace fltk ++} // namespace dw +\endcode + +at the end. Most important, the base class is changed: + +\code +-#include "FL/Fl_Widget.H" ++#include +\endcode + +and + +\code +-class FL_API ComplexButton : public Fl_Widget { ++class ComplexButton: public Fl_Group ++{ +\endcode + +Finally, for dw::fltk::ui::ComplexButton::default_style, there is a +namespace conflict: + +\code +- static NamedStyle* default_style; ++ static ::fltk::NamedStyle* default_style; +\endcode + +

Changes in fltkcomplexbutton.cc

+ +First, \#include's: + +\code + + #include +-#include // formerly + #include + #include ++ ++#include "fltkcomplexbutton.hh" +\endcode + +Second, namespaces: + +\code ++using namespace dw::fltk::ui; +\endcode + +Since the base class is now Fl_Group, the constructor must be changed: + +\code +-ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) : Fl_Widget(x,y,w,h,l) { ++ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) : ++ Fl_Group(x,y,w,h,l) ++{ +\endcode + +Finally, the button must draw its children (end of +dw::fltk::ui::ComplexButton::draw()): + +\code ++ ++ for (int i = children () - 1; i >= 0; i--) ++ draw_child (*child (i)); + } +\endcode + +*/ diff --git a/devdoc/index.doc b/devdoc/index.doc new file mode 100644 index 00000000..59de8cd8 --- /dev/null +++ b/devdoc/index.doc @@ -0,0 +1,48 @@ +/** \mainpage + +

Overview

+ +This is a list of documents to start with: + +
    +
  • \ref lout +
  • \ref dw-overview (map at \ref dw-map) +
+ +Currently, a document \ref fltk-problems is maintained, ideally, it +will be removed soon. + +

Historical

+ +

Replacements for GTK+ and GLib

+ +There are several classes etc., which are used for tasks formerly (in the GTK+ +version of dillo) achieved by GtkObject (in 1.2.x, this is part of Gtk+) and +GLib. For an overview on all this, take a look at \ref lout. + +GtkObject is replaced by the following: + +
    +
  • lout::object::Object is a common base class for many classes used + dillo. In the namespace lout::object, there are also some more common + classes and interfaces. + +
  • A sub class of lout::object::Object is + lout::identity::IdentifiableObject, which allows to determine the + class at run-time (equivalent to GTK_CHECK_CAST in GtkObject). + +
  • For signals, there is the namespace lout::signal. +
+ +Hash tables, linked lists etc. can be found in the lout::container namespace, +several useful macros from GLib have been implemented as inline functions +in the lout::misc namespace. + +As an alternative to the macros defined in list.h, there is also a template +class, lout::misc::SimpleVector, which does the same. + +

Changes in Dw

+ +If you have been familiar with Dw before, take a look at \ref dw-changes. + +*/ diff --git a/devdoc/lout.doc b/devdoc/lout.doc new file mode 100644 index 00000000..4e1503c6 --- /dev/null +++ b/devdoc/lout.doc @@ -0,0 +1,95 @@ +/** \page lout Lots of Useful Tools + +In the "lout" directory, there are some common base functionality for +C++. Most is described as doxygen comments, this text gives an +overview. + +

Common Base Class

+ +Many classes are derived from lout::object::Object, which defines some +general methods. See there for more information. + +For the case, that you need primitive C++ types, there are some +wrappers: + + +
C++ Type Wrapper Class +
void* lout::object::Pointer +
specific pointer lout::object::TypedPointer (template class) +
int lout::object::Integer +
const char* lout::object::ConstString +
char* lout::object::String +
+ + +

Containers

+ +In the namespace lout::container, several container classes are defined, +which all deal with instances of lout::object::Object. + +

Untyped Containers

+ +In lout::container::untyped, there are the following containers: + +
    +
  • lout::container::untyped::Vector, a dynamically increases array, +
  • lout::container::untyped::List, a linked list, +
  • lout::container::untyped::HashTable, a hash table, and +
  • lout::container::untyped::Stack, a stack. +
+ +All provide specific methods, but since they have a common base class, +lout::container::untyped::Collection, they all provide iterators, by the +method lout::container::untyped::Collection::iterator. + +

Typed Containers

+ +lout::container::typed provides wrappers for the container classes defined +in lout::container::untyped, which are more type safe, by using C++ +templates. + + +

Signals

+ +For how to connect objects at run-time (to reduce dependencies), take a +look at the lout::signal namespace. + +There is also a base class lout::signal::ObservedObject, which implements +signals for deletion. + + +

Debugging

+ +In debug.hh, there are some some useful macros for debugging messages, +see the file for mor informations. + + +

Identifying Classes at Runtime

+ +If the class of an object must be identified at runtime, +lout::identity::IdentifiableObject should be used as the base class, +see there for more details. + + +

Miscellaneous

+ +The lout::misc namespace provides several miscellaneous stuff: + +
    +
  • In some contexts, it is necessary to compare objects + (less/greater), for this, also lout::misc::Comparable must be + implemented. For example., lout::container::untyped::Vector::sort and + lout::container::typed::Vector::sort cast the elements to + lout::misc::Comparable. This can be mixed with lout::object::Object. +
  • lout::misc::SimpleVector, a simple, template based vector class + (not depending on lout::object::Object) (a variant for handling a + special case in an efficient way is lout::misc::NotSoSimpleVector), +
  • lout::misc::StringBuffer, class for fast concatenation of a large number + of strings, +
  • lout::misc::BitSet implements a bitset. +
  • useful (template) functions (lout::misc::min, lout::misc::max), and +
  • some functions useful for runtime checks (lout::misc::assert, + lout::misc::assertNotReached). +
+ +*/ diff --git a/devdoc/not-so-simple-container.png b/devdoc/not-so-simple-container.png new file mode 100644 index 00000000..0af067b5 Binary files /dev/null and b/devdoc/not-so-simple-container.png differ diff --git a/devdoc/rounding-errors.doc b/devdoc/rounding-errors.doc new file mode 100644 index 00000000..a442033e --- /dev/null +++ b/devdoc/rounding-errors.doc @@ -0,0 +1,35 @@ +/** \page rounding-errors How to Avoid Rounding Errors + +(Probably, this is a standard algorithm, so if someone knows the name, +drop me a note.) + +If something like + +\f[y_i = {x_i a \over b}\f] + +is to be calculated, and all numbers are integers, a naive +implementation would result in something, for which + +\f[\sum y_i \ne {(\sum x_i) a \over b}\f] + +because of rounding errors, due to the integer division. This can be +avoided by transforming the formula into + +\f[y_i = {(\sum_{j=0}^{j=i} x_j) a \over b} - \sum_{j=0}^{j=i-1} y_j\f] + +Of corse, when all \f$y_i\f$ are calculated in a sequence, +\f$\sum_{j=0}^{j=i} x_j\f$ and \f$\sum_{j=0}^{j=i-1} y_j\f$ can be +accumulated in the same loop. Regard this as sample: + +\code +int n, x[n], a, b; // Should all be initialized. +int y[n], cumX = 0, cumY = 0; + +for (int i = 0; i < n; i++) { + cumX += x[i] + y[i] = (cumX * a) / b - cumY; + cumY += y[i]; +} +\endcode + +*/ diff --git a/devdoc/uml-legend.doc b/devdoc/uml-legend.doc new file mode 100644 index 00000000..54004ccd --- /dev/null +++ b/devdoc/uml-legend.doc @@ -0,0 +1,195 @@ +/** \page uml-legend UML Legend + +This page describes the notation for several diagrams used in the +documentation, which is a slight variation of UML. + + +

Classes

+ +Classes are represented by boxes, containing there names: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + fontname=Helvetica; fontsize=8; + "Concrete Class"; + "Abstract Class" [color="#a0a0a0"]; + Interface [color="#ff8080"]; +} +\enddot + +(In most cases, the attributes and operations are left away, for +better readibility. Just click on it, to get to the detailed +description.) + +Of course, in C++, there are no interfaces, but here, we call a class, +which has only virtual abstract methods, and so does not provide any +functionality, an interface. + +Templates get a yellow background color: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10, + fillcolor="#ffffc0", style="filled"]; + fontname=Helvetica; fontsize=8; + "Concrete Class Template"; + "Abstract Class Template" [color="#a0a0a0"]; + "Interface Template" [color="#ff8080"]; +} +\enddot + + +

Objects

+ +In some cases, an examle for a concrete constellation of objects is +shown. An object is represented by a box containing a name and the +class, separated by a colon. + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10, + color="#404040", labelfontcolor="#000080"]; + fontname=Helvetica; fontsize=10; + + "x: A" -> "y1: B"; + "x: A" -> "y2: B"; +} +\enddot + +The names (\em x, \em y, and \em z) are only meant within the context +of the diagram, there needs not to be a relation to the actual names +in the program. They should be unique within the diagram. + +Classes and objects may be mixed in one diagram. + + +

Associations

+ +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10, + color="#404040", labelfontcolor="#000080", + fontname=Helvetica, fontsize=10, fontcolor="#000080"]; + fontname=Helvetica; fontsize=10; + A -> B [headlabel="*", taillabel="1", label="x"]; +} +\enddot + +In this example, one instance of A refers to an arbitrary number of B +instances (denoted by the "*"), and each instance of B is referred by +exactly one ("1") A. The label \em x is the name of the association, +in most cases the name of the field, e.g. A::x. + +Possible other values for the \em multiplicity: + +
    +
  • a concrete number, in most cases "1", +
  • a range, e.g. "0..1", +
  • "*", denoting an arbitrary number. +
+ + +

Implementations and Inheritance

+ +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="none", dir="both", arrowtail="empty", + labelfontname=Helvetica, labelfontsize=10, color="#404040", + labelfontcolor="#000080"]; + fontname=Helvetica; fontsize=10; + A[color="#ff8080"]; + B[color="#ff8080"]; + C; + D; + A -> B; + A -> C [style="dashed"]; + C -> D; +} +\enddot + +In this example, + +
    +
  • the interface B extends the interface A, +
  • the class C implements the interface A, and +
  • the class D extends the class C. +
+ + +

Template Instantiations

+ +Template instantiations are shown as own classes/interfaces, the +instantiation by the template is shown by a yellow dashed arrow: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="none", arrowtail="empty", dir="both", + labelfontname=Helvetica, labelfontsize=10, color="#404040", + labelfontcolor="#000080"]; + fontname=Helvetica; fontsize=10; + + A[color="#ff8080"]; + B[color="#ff8080"]; + C[color="#ff8080", fillcolor="#ffffc0", style="filled"]; + C_A[color="#ff8080", label="C \"]; + C_B[color="#ff8080", label="C \"]; + D; + + C -> C_A [arrowhead="open", arrowtail="none", style="dashed", + color="#808000"]; + C -> C_B [arrowhead="open", arrowtail="none", style="dashed", + color="#808000"]; + A -> C_A; + B -> C_B; + C_A -> D [style="dashed"]; +} +\enddot + +In this example, the interface template C uses the template argument +as super interface. + + +

Packages

+ +Packages are presented by dashed rectangles: + +\dot +digraph G { + node [shape=record, fontname=Helvetica, fontsize=10]; + edge [arrowhead="none", arrowtail="empty", dir="both", + labelfontname=Helvetica, labelfontsize=10, color="#404040", + labelfontcolor="#000080"]; + fontname=Helvetica; fontsize=10; + + subgraph cluster_1 { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + label="package 1"; + + A; + B [color="#a0a0a0"]; + } + + subgraph cluster_2 { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + label="package 2"; + + C; + D [color="#a0a0a0"]; + E + } + + A -> C; + B -> D; + D -> E; + E -> A [arrowhead="open", arrowtail="none"]; +} +\enddot + +Packages may be nested. + +*/ \ No newline at end of file -- cgit v1.2.3 From 77e49d91f226b5a4bde86bd52fb7793665906992 Mon Sep 17 00:00:00 2001 From: Sebastian Geerken Date: Mon, 1 Jun 2015 22:23:47 +0200 Subject: NotSoSimpleVector: nicer image --- devdoc/not-so-simple-container.png | Bin 5738 -> 19319 bytes devdoc/not-so-simple-container.svg | 785 +++++++++++++++++++++++++++++++++++++ 2 files changed, 785 insertions(+) create mode 100644 devdoc/not-so-simple-container.svg (limited to 'devdoc') diff --git a/devdoc/not-so-simple-container.png b/devdoc/not-so-simple-container.png index 0af067b5..f3e2c039 100644 Binary files a/devdoc/not-so-simple-container.png and b/devdoc/not-so-simple-container.png differ diff --git a/devdoc/not-so-simple-container.svg b/devdoc/not-so-simple-container.svg new file mode 100644 index 00000000..ce00510e --- /dev/null +++ b/devdoc/not-so-simple-container.svg @@ -0,0 +1,785 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + unaffected (in main array) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + main array (moved) + original extra array + new inserted area + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Example 1: + + Example 2: + + -- cgit v1.2.3