mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-16 07:27:35 +00:00
window_layouter: modernized coding style
Replace the use of pointers and the copying of XML nodes by the 'with_' pattern. Use plain struct where appropriate, replace constructors by { } initialization. Use C++20 function template syntax. Replace accessors by public constants where possible. Follow 'from_xml' convention. Follow usual 'Action' interface naming. Issue #5390
This commit is contained in:
parent
ca1b22b0aa
commit
dbcc21c109
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* \brief Action triggered by the user
|
||||
* \author Norman Feske
|
||||
* \date 2016-02-01
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _ACTION_H_
|
||||
#define _ACTION_H_
|
||||
|
||||
#include <target.h>
|
||||
|
||||
namespace Window_layouter { class Action; }
|
||||
|
||||
|
||||
/**
|
||||
* Result of the application of a key event to the key-sequence tracker
|
||||
*/
|
||||
class Window_layouter::Action
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
NONE,
|
||||
NEXT_WINDOW,
|
||||
PREV_WINDOW,
|
||||
RAISE_WINDOW,
|
||||
TOGGLE_FULLSCREEN,
|
||||
CLOSE,
|
||||
NEXT_WORKSPACE,
|
||||
PREV_WORKSPACE,
|
||||
MARK,
|
||||
DETACH,
|
||||
ATTACH,
|
||||
COLUMN,
|
||||
ROW,
|
||||
REMOVE,
|
||||
NEXT_COLUMN,
|
||||
PREV_COLUMN,
|
||||
NEXT_ROW,
|
||||
PREV_ROW,
|
||||
NEXT_TAB,
|
||||
PREV_TAB,
|
||||
TOOGLE_OVERLAY,
|
||||
SCREEN,
|
||||
RELEASE_GRAB,
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Type _type;
|
||||
Target::Name _target;
|
||||
|
||||
template <Genode::size_t N>
|
||||
static Type _type_by_string(String<N> const &string)
|
||||
{
|
||||
if (string == "next_window") return NEXT_WINDOW;
|
||||
if (string == "prev_window") return PREV_WINDOW;
|
||||
if (string == "raise_window") return RAISE_WINDOW;
|
||||
if (string == "toggle_fullscreen") return TOGGLE_FULLSCREEN;
|
||||
if (string == "screen") return SCREEN;
|
||||
if (string == "release_grab") return RELEASE_GRAB;
|
||||
|
||||
Genode::warning("cannot convert \"", string, "\" to action type");
|
||||
return NONE;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Action(Type type) : _type(type), _target() { }
|
||||
|
||||
template <Genode::size_t N>
|
||||
Action(String<N> const &string, Target::Name const &arg)
|
||||
: _type(_type_by_string(string)), _target(arg) { }
|
||||
|
||||
Type type() const { return _type; }
|
||||
Target::Name target_name() const { return _target; }
|
||||
};
|
||||
|
||||
#endif /* _ACTION_H_ */
|
@ -42,17 +42,63 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
: Registry<Member>::Element(registry, *this), window(window) { }
|
||||
};
|
||||
|
||||
using Label = String<80>;
|
||||
|
||||
Target::Name target_name { };
|
||||
|
||||
private:
|
||||
|
||||
Registry<Member> _members { };
|
||||
|
||||
struct Label
|
||||
{
|
||||
using Value = String<80>;
|
||||
|
||||
Value exact, prefix, suffix;
|
||||
|
||||
static Label from_xml(Xml_node const &node)
|
||||
{
|
||||
return { .exact = node.attribute_value("label", Value()),
|
||||
.prefix = node.attribute_value("label_prefix", Value()),
|
||||
.suffix = node.attribute_value("label_suffix", Value()) };
|
||||
}
|
||||
|
||||
bool operator == (Label const &other) const
|
||||
{
|
||||
return exact == other.exact
|
||||
&& prefix == other.prefix
|
||||
&& suffix == other.suffix;
|
||||
}
|
||||
|
||||
bool matches(Value const &label) const
|
||||
{
|
||||
if (exact.valid() && label == exact)
|
||||
return true;
|
||||
|
||||
if (exact.valid())
|
||||
return false;
|
||||
|
||||
bool const prefix_matches =
|
||||
prefix.valid() &&
|
||||
!strcmp(label.string(), prefix.string(), prefix.length() - 1);
|
||||
|
||||
bool suffix_matches = false;
|
||||
if (label.length() >= suffix.length()) {
|
||||
size_t const offset = label.length() - suffix.length();
|
||||
suffix_matches = !strcmp(label.string() + offset, suffix.string());
|
||||
}
|
||||
|
||||
return (!prefix.valid() || prefix_matches)
|
||||
&& (!suffix.valid() || suffix_matches);
|
||||
}
|
||||
|
||||
void gen_attr(Xml_generator &xml) const
|
||||
{
|
||||
if (exact .valid()) xml.attribute("label", exact);
|
||||
if (prefix.valid()) xml.attribute("label_prefix", prefix);
|
||||
if (suffix.valid()) xml.attribute("label_suffix", suffix);
|
||||
}
|
||||
};
|
||||
|
||||
Label const _label;
|
||||
Label const _label_prefix;
|
||||
Label const _label_suffix;
|
||||
|
||||
bool _pos_defined = false;
|
||||
bool _xpos_any = false;
|
||||
@ -66,12 +112,7 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
|
||||
public:
|
||||
|
||||
Assign(Xml_node assign)
|
||||
:
|
||||
_label (assign.attribute_value("label", Label())),
|
||||
_label_prefix(assign.attribute_value("label_prefix", Label())),
|
||||
_label_suffix(assign.attribute_value("label_suffix", Label()))
|
||||
{ }
|
||||
Assign(Xml_node assign) : _label(Label::from_xml(assign)) { }
|
||||
|
||||
void update(Xml_node assign)
|
||||
{
|
||||
@ -89,19 +130,12 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
bool matches(Xml_node node) const
|
||||
{
|
||||
return node.attribute_value("label", Label()) == _label
|
||||
&& node.attribute_value("label_prefix", Label()) == _label_prefix
|
||||
&& node.attribute_value("label_suffix", Label()) == _label_suffix;
|
||||
}
|
||||
bool matches(Xml_node node) const { return Label::from_xml(node) == _label; }
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
static bool type_matches(Xml_node const &node)
|
||||
{
|
||||
return node.has_type("assign");
|
||||
static bool type_matches(Xml_node const &node) { return node.has_type("assign");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,38 +170,19 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
*
|
||||
* This method is used for associating assignments to windows.
|
||||
*/
|
||||
template <typename FN>
|
||||
void with_matching_members_registry(Label const &label, FN const &fn)
|
||||
void with_matching_members_registry(Label::Value const &label, auto const &fn)
|
||||
{
|
||||
bool const label_matches = (_label.valid() && label == _label);
|
||||
|
||||
bool const prefix_matches =
|
||||
_label_prefix.valid() &&
|
||||
!strcmp(label.string(),
|
||||
_label_prefix.string(), _label_prefix.length() - 1);
|
||||
|
||||
bool suffix_matches = false;
|
||||
if (label.length() >= _label_suffix.length()) {
|
||||
unsigned const offset = (unsigned)(label.length() - _label_suffix.length());
|
||||
suffix_matches = !strcmp(_label.string() + offset, _label_suffix.string());
|
||||
}
|
||||
|
||||
bool const wildcard_matches = !_label.valid()
|
||||
&& (!_label_prefix.valid() || prefix_matches)
|
||||
&& (!_label_suffix.valid() || suffix_matches);
|
||||
|
||||
if (label_matches || wildcard_matches)
|
||||
if (_label.matches(label))
|
||||
fn(_members);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate <assign> nodes of windows captured via wildcard
|
||||
*/
|
||||
template <typename FN>
|
||||
void for_each_wildcard_member(FN const &fn) const
|
||||
void for_each_wildcard_member(auto const &fn) const
|
||||
{
|
||||
/* skip non-wildcards */
|
||||
if (_label.valid())
|
||||
if (_label.exact.valid())
|
||||
return;
|
||||
|
||||
_members.for_each([&] (Assign::Member const &member) { fn(member); });
|
||||
@ -176,10 +191,9 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
/**
|
||||
* Used to bring wildcard-matching windows to front
|
||||
*/
|
||||
template <typename FN>
|
||||
void for_each_wildcard_member(FN const &fn)
|
||||
void for_each_wildcard_member(auto const &fn)
|
||||
{
|
||||
if (_label.valid())
|
||||
if (_label.exact.valid())
|
||||
return;
|
||||
|
||||
_members.for_each([&] (Assign::Member &member) { fn(member); });
|
||||
@ -187,17 +201,14 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
|
||||
bool floating() const { return _pos_defined; }
|
||||
|
||||
bool wildcard() const { return !_label.valid(); }
|
||||
bool wildcard() const { return !_label.exact.valid(); }
|
||||
|
||||
/**
|
||||
* Generate <assign> node
|
||||
*/
|
||||
void gen_assign_attr(Xml_generator &xml) const
|
||||
{
|
||||
if (_label.valid()) xml.attribute("label", _label);
|
||||
if (_label_prefix.valid()) xml.attribute("label_prefix", _label_prefix);
|
||||
if (_label_suffix.valid()) xml.attribute("label_suffix", _label_suffix);
|
||||
|
||||
_label.gen_attr(xml);
|
||||
xml.attribute("target", target_name);
|
||||
}
|
||||
|
||||
@ -247,11 +258,8 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
xml.attribute("visible", "no");
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void for_each_member(FN const &fn) { _members.for_each(fn); }
|
||||
|
||||
template <typename FN>
|
||||
void for_each_member(FN const &fn) const { _members.for_each(fn); }
|
||||
void for_each_member(auto const &fn) { _members.for_each(fn); }
|
||||
void for_each_member(auto const &fn) const { _members.for_each(fn); }
|
||||
};
|
||||
|
||||
#endif /* _ASSIGN_H_ */
|
||||
|
@ -55,21 +55,19 @@ class Window_layouter::Assign_list : Noncopyable
|
||||
auto fn = [&] (Registry<Assign::Member> ®istry) {
|
||||
window.assignment(registry); };
|
||||
|
||||
assign.with_matching_members_registry(window.label(), fn);
|
||||
assign.with_matching_members_registry(window.label, fn);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void for_each_wildcard_member(FN const &fn) const
|
||||
void for_each_wildcard_member(auto const &fn) const
|
||||
{
|
||||
_assignments.for_each([&] (Assign const &assign) {
|
||||
assign.for_each_wildcard_member([&] (Assign::Member const &member) {
|
||||
fn(assign, member); }); });
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void for_each_wildcard_assigned_window(FN const &fn)
|
||||
void for_each_wildcard_assigned_window(auto const &fn)
|
||||
{
|
||||
_assignments.for_each([&] (Assign &assign) {
|
||||
assign.for_each_wildcard_member([&] (Assign::Member &member) {
|
||||
|
52
repos/gems/src/app/window_layouter/command.h
Normal file
52
repos/gems/src/app/window_layouter/command.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* \brief Command triggered via the keyboard
|
||||
* \author Norman Feske
|
||||
* \date 2016-02-01
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _COMMAND_H_
|
||||
#define _COMMAND_H_
|
||||
|
||||
#include <target.h>
|
||||
|
||||
namespace Window_layouter { class Command; }
|
||||
|
||||
|
||||
struct Window_layouter::Command
|
||||
{
|
||||
enum Type { NONE, NEXT_WINDOW, PREV_WINDOW, RAISE_WINDOW, TOGGLE_FULLSCREEN,
|
||||
NEXT_TAB, PREV_TAB, SCREEN, RELEASE_GRAB, };
|
||||
|
||||
Type type;
|
||||
Target::Name target;
|
||||
|
||||
static Command from_xml(Xml_node const &node)
|
||||
{
|
||||
auto from_string = [] (auto const &string) -> Type
|
||||
{
|
||||
if (string == "next_window") return NEXT_WINDOW;
|
||||
if (string == "prev_window") return PREV_WINDOW;
|
||||
if (string == "raise_window") return RAISE_WINDOW;
|
||||
if (string == "toggle_fullscreen") return TOGGLE_FULLSCREEN;
|
||||
if (string == "screen") return SCREEN;
|
||||
if (string == "release_grab") return RELEASE_GRAB;
|
||||
|
||||
warning("cannot convert \"", string, "\" to action type");
|
||||
return NONE;
|
||||
};
|
||||
|
||||
return {
|
||||
.type = from_string(node.attribute_value("action", String<32>())),
|
||||
.target = node.attribute_value("target", Name())
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _COMMAND_H_ */
|
@ -24,13 +24,13 @@ struct Window_layouter::Decorator_margins
|
||||
{
|
||||
unsigned top, bottom, left, right;
|
||||
|
||||
Decorator_margins(Xml_node node)
|
||||
:
|
||||
top (node.attribute_value("top", 0U)),
|
||||
bottom(node.attribute_value("bottom", 0U)),
|
||||
left (node.attribute_value("left", 0U)),
|
||||
right (node.attribute_value("right", 0U))
|
||||
{ }
|
||||
static Decorator_margins from_xml(Xml_node const &node)
|
||||
{
|
||||
return { .top = node.attribute_value("top", 0U),
|
||||
.bottom = node.attribute_value("bottom", 0U),
|
||||
.left = node.attribute_value("left", 0U),
|
||||
.right = node.attribute_value("right", 0U) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert outer geometry to inner geometry
|
||||
|
@ -15,7 +15,7 @@
|
||||
#define _KEY_SEQUENCE_TRACKER_H_
|
||||
|
||||
/* local includes */
|
||||
#include "action.h"
|
||||
#include <command.h>
|
||||
|
||||
namespace Window_layouter { class Key_sequence_tracker; }
|
||||
|
||||
@ -28,20 +28,18 @@ class Window_layouter::Key_sequence_tracker
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
enum Type { PRESS, RELEASE };
|
||||
bool press;
|
||||
|
||||
Type type = PRESS;
|
||||
|
||||
Input::Keycode keycode = Input::KEY_UNKNOWN;
|
||||
|
||||
Entry() { }
|
||||
|
||||
Entry(Type type, Input::Keycode keycode)
|
||||
: type(type), keycode(keycode) { }
|
||||
Input::Keycode key;
|
||||
|
||||
bool operator == (Entry const &other) const
|
||||
{
|
||||
return other.type == type && other.keycode == keycode;
|
||||
return other.press == press && other.key == key;
|
||||
}
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
Genode::print(out, press ? "press " : "release ", Input::key_name(key));
|
||||
}
|
||||
};
|
||||
|
||||
@ -50,7 +48,7 @@ class Window_layouter::Key_sequence_tracker
|
||||
* sequence.
|
||||
*/
|
||||
enum { MAX_ENTRIES = 64 };
|
||||
Entry entries[MAX_ENTRIES];
|
||||
Entry entries[MAX_ENTRIES] { };
|
||||
|
||||
unsigned pos = 0;
|
||||
|
||||
@ -91,82 +89,69 @@ class Window_layouter::Key_sequence_tracker
|
||||
}
|
||||
|
||||
void reset() { pos = 0; }
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
Genode::print(out, "[", pos, "]: ");
|
||||
for (unsigned i = 0; i < pos; i++)
|
||||
Genode::print(out, " ", entries[i]);
|
||||
}
|
||||
};
|
||||
|
||||
Stack _stack { };
|
||||
|
||||
Xml_node _matching_sub_node(Xml_node curr, Stack::Entry entry)
|
||||
void _with_matching_sub_node(Xml_node const &curr, Stack::Entry entry,
|
||||
auto const &fn, auto const &no_match_fn) const
|
||||
{
|
||||
char const *node_type = entry.type == Stack::Entry::PRESS
|
||||
? "press" : "release";
|
||||
auto const node_type = entry.press ? "press" : "release";
|
||||
|
||||
using Key_name = String<32>;
|
||||
Key_name const key(Input::key_name(entry.keycode));
|
||||
|
||||
Xml_node result("<none/>");
|
||||
Key_name const key(Input::key_name(entry.key));
|
||||
|
||||
bool done = false; /* process the first match only */
|
||||
curr.for_each_sub_node(node_type, [&] (Xml_node const &node) {
|
||||
if (!done && node.attribute_value("key", Key_name()) == key) {
|
||||
fn(node);
|
||||
done = true; } });
|
||||
|
||||
if (node.attribute_value("key", Key_name()) != key)
|
||||
return;
|
||||
|
||||
/* set 'result' only once, so we return the first match */
|
||||
if (result.has_type("none"))
|
||||
result = node;
|
||||
});
|
||||
|
||||
return result;
|
||||
if (!done)
|
||||
no_match_fn();
|
||||
}
|
||||
|
||||
void _with_match_rec(unsigned const pos, Xml_node const &node, auto const &fn) const
|
||||
{
|
||||
if (pos == _stack.pos) {
|
||||
fn(node);
|
||||
return;
|
||||
}
|
||||
|
||||
/* recursion is bounded by Stack::MAX_ENTRIES */
|
||||
_with_matching_sub_node(node, _stack.entries[pos],
|
||||
[&] (Xml_node const &sub_node) {
|
||||
if (pos < _stack.pos)
|
||||
_with_match_rec(pos + 1, sub_node, fn); },
|
||||
[&] { });
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup XML node that matches the state of the key sequence
|
||||
* Call 'fn' with XML node that matches the state of the key sequence
|
||||
*
|
||||
* Traverse the nested '<press>' and '<release>' nodes of the
|
||||
* configuration according to the history of events of the current
|
||||
* sequence.
|
||||
*
|
||||
* \return XML node of the type '<press>' or '<release>'.
|
||||
* If the configuration does not contain a matching node, the
|
||||
* method returns a dummy node '<none>'.
|
||||
*/
|
||||
Xml_node _xml_by_path(Xml_node config)
|
||||
void _with_xml_by_path(Xml_node const &config, auto const &fn) const
|
||||
{
|
||||
Xml_node curr = config;
|
||||
|
||||
/*
|
||||
* Each iteration corresponds to a nesting level
|
||||
*/
|
||||
for (unsigned i = 0; i < _stack.pos; i++) {
|
||||
|
||||
Stack::Entry const entry = _stack.entries[i];
|
||||
|
||||
Xml_node const match = _matching_sub_node(curr, entry);
|
||||
|
||||
if (match.has_type("none"))
|
||||
return match;
|
||||
|
||||
curr = match;
|
||||
}
|
||||
|
||||
return curr;
|
||||
_with_match_rec(0, config, fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute action denoted in the specific XML node
|
||||
* Execute command denoted in the specific XML node
|
||||
*/
|
||||
template <typename FUNC>
|
||||
void _execute_action(Xml_node node, FUNC const &func)
|
||||
void _execute_command(Xml_node node, auto const &fn)
|
||||
{
|
||||
if (!node.has_attribute("action"))
|
||||
return;
|
||||
|
||||
using Action = String<32>;
|
||||
Action action = node.attribute_value("action", Action());
|
||||
|
||||
using Name = Window_layouter::Target::Name;
|
||||
Name const target = node.attribute_value("target", Name());
|
||||
|
||||
func(Window_layouter::Action(action, target));
|
||||
if (node.has_attribute("action"))
|
||||
fn(Command::from_xml(node));
|
||||
}
|
||||
|
||||
public:
|
||||
@ -179,13 +164,12 @@ class Window_layouter::Key_sequence_tracker
|
||||
/**
|
||||
* Apply event to key sequence
|
||||
*
|
||||
* \param func functor to be called if the event leads to a node in
|
||||
* the key-sequence configuration and the node is
|
||||
* equipped with an 'action' attribute. The functor is
|
||||
* called with an 'Action' as argument.
|
||||
* \param fn functor to be called if the event leads to a node in
|
||||
* the key-sequence configuration and the node is
|
||||
* equipped with an 'action' attribute. The functor is
|
||||
* called with an 'Action' as argument.
|
||||
*/
|
||||
template <typename FUNC>
|
||||
void apply(Input::Event const &ev, Xml_node config, FUNC const &func)
|
||||
void apply(Input::Event const &ev, Xml_node const &config, auto const &fn)
|
||||
{
|
||||
/*
|
||||
* If the sequence contains a press-release combination for
|
||||
@ -194,44 +178,44 @@ class Window_layouter::Key_sequence_tracker
|
||||
* once.
|
||||
*/
|
||||
ev.handle_press([&] (Input::Keycode key, Codepoint) {
|
||||
_stack.flush(Stack::Entry(Stack::Entry::PRESS, key));
|
||||
_stack.flush(Stack::Entry(Stack::Entry::RELEASE, key));
|
||||
_stack.flush(Stack::Entry { .press = true, .key = key });
|
||||
_stack.flush(Stack::Entry { .press = false, .key = key });
|
||||
});
|
||||
|
||||
Xml_node curr_node = _xml_by_path(config);
|
||||
_with_xml_by_path(config, [&] (Xml_node const &curr_node) {
|
||||
|
||||
ev.handle_press([&] (Input::Keycode key, Codepoint) {
|
||||
ev.handle_press([&] (Input::Keycode key, Codepoint) {
|
||||
|
||||
Stack::Entry const entry(Stack::Entry::PRESS, key);
|
||||
Stack::Entry const press { .press = true, .key = key };
|
||||
|
||||
_execute_action(_matching_sub_node(curr_node, entry), func);
|
||||
_stack.push(entry);
|
||||
});
|
||||
_with_matching_sub_node(curr_node, press,
|
||||
[&] (Xml_node const &node) { _execute_command(node, fn); },
|
||||
[&] { });
|
||||
|
||||
ev.handle_release([&] (Input::Keycode key) {
|
||||
_stack.push(press);
|
||||
});
|
||||
|
||||
Stack::Entry const entry(Stack::Entry::RELEASE, key);
|
||||
ev.handle_release([&] (Input::Keycode key) {
|
||||
|
||||
Xml_node const next_node = _matching_sub_node(curr_node, entry);
|
||||
Stack::Entry const release { .press = false, .key = key };
|
||||
|
||||
/*
|
||||
* If there exists a specific path for the release event,
|
||||
* follow the path. Otherwise, we remove the released key from
|
||||
* the sequence.
|
||||
*/
|
||||
if (!next_node.has_type("none")) {
|
||||
|
||||
_execute_action(next_node, func);
|
||||
_stack.push(entry);
|
||||
|
||||
} else {
|
||||
|
||||
Stack::Entry entry(Stack::Entry::PRESS, key);
|
||||
_stack.flush(entry);
|
||||
}
|
||||
/*
|
||||
* If there exists a specific path for the release event,
|
||||
* follow the path. Otherwise, we remove the released key
|
||||
* from the sequence.
|
||||
*/
|
||||
_with_matching_sub_node(curr_node, release,
|
||||
[&] (Xml_node const &next_node) {
|
||||
_execute_command(next_node, fn);
|
||||
_stack.push(release);
|
||||
},
|
||||
[&] /* no match */ {
|
||||
Stack::Entry const press { .press = true, .key = key };
|
||||
_stack.flush(press);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _KEY_SEQUENCE_TRACKER_H_ */
|
||||
|
@ -28,7 +28,7 @@ class Window_layouter::Layout_rules : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
struct Change_handler : Interface
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void layout_rules_changed() = 0;
|
||||
};
|
||||
@ -39,7 +39,7 @@ class Window_layouter::Layout_rules : Noncopyable
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Change_handler &_change_handler;
|
||||
Action &_action;
|
||||
|
||||
Constructible<Buffered_xml> _config_rules { };
|
||||
|
||||
@ -49,19 +49,19 @@ class Window_layouter::Layout_rules : Noncopyable
|
||||
|
||||
Attached_rom_dataspace rom;
|
||||
|
||||
Change_handler &_change_handler;
|
||||
Action &_action;
|
||||
|
||||
void _handle()
|
||||
{
|
||||
rom.update();
|
||||
_change_handler.layout_rules_changed();
|
||||
_action.layout_rules_changed();
|
||||
}
|
||||
|
||||
Signal_handler<Rom_rules> _handler;
|
||||
|
||||
Rom_rules(Env &env, Change_handler &change_handler)
|
||||
Rom_rules(Env &env, Action &action)
|
||||
:
|
||||
rom(env, node_type()), _change_handler(change_handler),
|
||||
rom(env, node_type()), _action(action),
|
||||
_handler(env.ep(), *this, &Rom_rules::_handle)
|
||||
{
|
||||
rom.sigh(_handler);
|
||||
@ -73,9 +73,9 @@ class Window_layouter::Layout_rules : Noncopyable
|
||||
|
||||
public:
|
||||
|
||||
Layout_rules(Env &env, Allocator &alloc, Change_handler &change_handler)
|
||||
Layout_rules(Env &env, Allocator &alloc, Action &action)
|
||||
:
|
||||
_env(env), _alloc(alloc), _change_handler(change_handler)
|
||||
_env(env), _alloc(alloc), _action(action)
|
||||
{ }
|
||||
|
||||
void update_config(Xml_node config)
|
||||
@ -83,14 +83,14 @@ class Window_layouter::Layout_rules : Noncopyable
|
||||
bool const use_rules_from_rom =
|
||||
(config.attribute_value(Rom_rules::node_type(), String<10>()) == "rom");
|
||||
|
||||
_rom_rules.conditional(use_rules_from_rom, _env, _change_handler);
|
||||
_rom_rules.conditional(use_rules_from_rom, _env, _action);
|
||||
|
||||
_config_rules.destruct();
|
||||
|
||||
if (config.has_sub_node(Rom_rules::node_type()))
|
||||
_config_rules.construct(_alloc, config.sub_node(Rom_rules::node_type()));
|
||||
|
||||
_change_handler.layout_rules_changed();
|
||||
_action.layout_rules_changed();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,8 +102,7 @@ class Window_layouter::Layout_rules : Noncopyable
|
||||
* definitions are present, the rules ROM - if valid - takes
|
||||
* precedence over the configuration's '<rules>' node.
|
||||
*/
|
||||
template <typename FN>
|
||||
void with_rules(FN const &fn) const
|
||||
void with_rules(auto const &fn) const
|
||||
{
|
||||
if (_rom_rules.constructed()) {
|
||||
Xml_node const rules = _rom_rules->rom.xml();
|
||||
|
@ -30,14 +30,13 @@
|
||||
#include <assign_list.h>
|
||||
#include <target_list.h>
|
||||
#include <user_state.h>
|
||||
#include <operations.h>
|
||||
|
||||
namespace Window_layouter { struct Main; }
|
||||
|
||||
|
||||
struct Window_layouter::Main : Operations,
|
||||
Layout_rules::Change_handler,
|
||||
Window_list::Change_handler
|
||||
struct Window_layouter::Main : User_state::Action,
|
||||
Layout_rules::Action,
|
||||
Window_list::Action
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
@ -63,7 +62,7 @@ struct Window_layouter::Main : Operations,
|
||||
|
||||
Layout_rules _layout_rules { _env, _heap, *this };
|
||||
|
||||
Decorator_margins _decorator_margins { Xml_node("<floating/>") };
|
||||
Decorator_margins _decorator_margins { };
|
||||
|
||||
Window_list _window_list {
|
||||
_env, _heap, *this, _focus_history, _decorator_margins };
|
||||
@ -104,17 +103,17 @@ struct Window_layouter::Main : Operations,
|
||||
_assign_list.for_each([&] (Assign &assign) {
|
||||
_target_list.for_each([&] (Target const &target) {
|
||||
|
||||
if (target.name() != assign.target_name)
|
||||
if (target.name != assign.target_name)
|
||||
return;
|
||||
|
||||
assign.for_each_member([&] (Assign::Member &member) {
|
||||
|
||||
member.window.floating(assign.floating());
|
||||
member.window.target_area(target.geometry().area);
|
||||
member.window.target_area(target.rect.area);
|
||||
|
||||
Rect const rect = assign.window_geometry(member.window.id().value,
|
||||
Rect const rect = assign.window_geometry(member.window.id.value,
|
||||
member.window.client_size(),
|
||||
target.geometry().area,
|
||||
target.rect.area,
|
||||
_decorator_margins);
|
||||
member.window.outer_geometry(rect);
|
||||
member.window.maximized(assign.maximized());
|
||||
@ -135,7 +134,7 @@ struct Window_layouter::Main : Operations,
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout_rules::Change_handler interface
|
||||
* Layout_rules::Action interface
|
||||
*/
|
||||
void layout_rules_changed() override
|
||||
{
|
||||
@ -145,7 +144,7 @@ struct Window_layouter::Main : Operations,
|
||||
}
|
||||
|
||||
/**
|
||||
* Window_list::Change_handler interface
|
||||
* Window_list::Action interface
|
||||
*/
|
||||
void window_list_changed() override { _update_window_layout(); }
|
||||
|
||||
@ -165,9 +164,9 @@ struct Window_layouter::Main : Operations,
|
||||
User_state _user_state { *this, _focus_history };
|
||||
|
||||
|
||||
/**************************
|
||||
** Operations interface **
|
||||
**************************/
|
||||
/**********************************
|
||||
** User_state::Action interface **
|
||||
**********************************/
|
||||
|
||||
void close(Window_id id) override
|
||||
{
|
||||
@ -226,11 +225,11 @@ struct Window_layouter::Main : Operations,
|
||||
_assign_list.for_each([&] (Assign &assign) {
|
||||
Window *window_ptr = nullptr;
|
||||
assign.for_each_member([&] (Assign::Member &member) {
|
||||
if (member.window.id() == id)
|
||||
if (member.window.id == id)
|
||||
window_ptr = &member.window; });
|
||||
if (window_ptr) {
|
||||
assign.target_name = to.name();
|
||||
window_ptr->warp(from.geometry().at - to.geometry().at);
|
||||
assign.target_name = to.name;
|
||||
window_ptr->warp(from.rect.at - to.rect.at);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -240,7 +239,7 @@ struct Window_layouter::Main : Operations,
|
||||
/* change of screen under the dragged window */
|
||||
if (_drag.dragging())
|
||||
_with_target_change(_drag.window_id, name, [&] (Target const &from, Target const &to) {
|
||||
if (from.geometry() == to.geometry())
|
||||
if (from.rect == to.rect)
|
||||
_retarget_window(_drag.window_id, from, to); });
|
||||
|
||||
_gen_rules_with_frontmost_screen(name);
|
||||
@ -253,7 +252,7 @@ struct Window_layouter::Main : Operations,
|
||||
|
||||
bool const moving = _moving(id, element);
|
||||
_target_list.with_target_at(curr, [&] (Target const &pointed) {
|
||||
_drag = { Drag::State::DRAGGING, moving, id, curr, pointed.geometry() }; });
|
||||
_drag = { Drag::State::DRAGGING, moving, id, curr, pointed.rect }; });
|
||||
|
||||
to_front(id);
|
||||
|
||||
@ -319,8 +318,8 @@ struct Window_layouter::Main : Operations,
|
||||
bool const moving = _moving(id, element);
|
||||
if (moving) {
|
||||
_target_list.with_target_at(curr, [&] (Target const &pointed) {
|
||||
_drag = { Drag::State::SETTLING, moving, id, curr, pointed.geometry() };
|
||||
_with_target_change(id, pointed.name(), [&] (Target const &from, Target const &to) {
|
||||
_drag = { Drag::State::SETTLING, moving, id, curr, pointed.rect };
|
||||
_with_target_change(id, pointed.name, [&] (Target const &from, Target const &to) {
|
||||
_retarget_window(id, from, to); });
|
||||
});
|
||||
}
|
||||
@ -359,8 +358,8 @@ struct Window_layouter::Main : Operations,
|
||||
_decorator_margins_rom.update();
|
||||
|
||||
Xml_node const margins = _decorator_margins_rom.xml();
|
||||
if (margins.has_sub_node("floating"))
|
||||
_decorator_margins = Decorator_margins(margins.sub_node("floating"));
|
||||
margins.with_optional_sub_node("floating", [&] (Xml_node const floating) {
|
||||
_decorator_margins = Decorator_margins::from_xml(floating); });
|
||||
|
||||
/* respond to change by adapting the maximized window geometry */
|
||||
_handle_mode_change();
|
||||
@ -424,8 +423,7 @@ struct Window_layouter::Main : Operations,
|
||||
_gen_rules_with_frontmost_screen(Target::Name());
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void _gen_rules_assignments(Xml_generator &, FN const &);
|
||||
void _gen_rules_assignments(Xml_generator &, auto const &);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -461,11 +459,11 @@ void Window_layouter::Main::_gen_window_layout()
|
||||
/* update hover and focus state of each window */
|
||||
_window_list.for_each_window([&] (Window &window) {
|
||||
|
||||
window.focused(window.has_id(_user_state.focused_window_id()));
|
||||
window.focused(window.id == _user_state.focused_window_id());
|
||||
|
||||
bool const hovered = window.has_id(_user_state.hover_state().window_id);
|
||||
bool const hovered = (window.id == _user_state.hover_state().window_id);
|
||||
window.hovered(hovered ? _user_state.hover_state().element
|
||||
: Window::Element::UNDEFINED);
|
||||
: Window::Element { });
|
||||
});
|
||||
|
||||
_window_layout_reporter.generate([&] (Xml_generator &xml) {
|
||||
@ -502,8 +500,7 @@ void Window_layouter::Main::_gen_focus()
|
||||
}
|
||||
|
||||
|
||||
template <typename FN>
|
||||
void Window_layouter::Main::_gen_rules_assignments(Xml_generator &xml, FN const &filter)
|
||||
void Window_layouter::Main::_gen_rules_assignments(Xml_generator &xml, auto const &filter_fn)
|
||||
{
|
||||
auto gen_window_geometry = [] (Xml_generator &xml,
|
||||
Assign const &assign, Window const &window) {
|
||||
@ -517,11 +514,11 @@ void Window_layouter::Main::_gen_rules_assignments(Xml_generator &xml, FN const
|
||||
/* turn wildcard assignments into exact assignments */
|
||||
auto fn = [&] (Assign const &assign, Assign::Member const &member) {
|
||||
|
||||
if (!filter(member.window))
|
||||
if (!filter_fn(member.window))
|
||||
return;
|
||||
|
||||
xml.node("assign", [&] () {
|
||||
xml.attribute("label", member.window.label());
|
||||
xml.attribute("label", member.window.label);
|
||||
xml.attribute("target", assign.target_name);
|
||||
gen_window_geometry(xml, assign, member.window);
|
||||
});
|
||||
@ -546,7 +543,7 @@ void Window_layouter::Main::_gen_rules_assignments(Xml_generator &xml, FN const
|
||||
|
||||
assign.for_each_member([&] (Assign::Member const &member) {
|
||||
|
||||
if (geometry_generated || !filter(member.window))
|
||||
if (geometry_generated || !filter_fn(member.window))
|
||||
return;
|
||||
|
||||
xml.node("assign", [&] () {
|
||||
@ -618,69 +615,20 @@ void Window_layouter::Main::_gen_rules_with_frontmost_screen(Target::Name const
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine window element that corresponds to hover model
|
||||
*/
|
||||
static Window_layouter::Window::Element
|
||||
_element_from_hover_model(Genode::Xml_node hover_window_xml)
|
||||
{
|
||||
using Type = Window_layouter::Window::Element::Type;
|
||||
|
||||
bool const left_sizer = hover_window_xml.has_sub_node("left_sizer"),
|
||||
right_sizer = hover_window_xml.has_sub_node("right_sizer"),
|
||||
top_sizer = hover_window_xml.has_sub_node("top_sizer"),
|
||||
bottom_sizer = hover_window_xml.has_sub_node("bottom_sizer");
|
||||
|
||||
if (left_sizer && top_sizer) return Type::TOP_LEFT;
|
||||
if (left_sizer && bottom_sizer) return Type::BOTTOM_LEFT;
|
||||
if (left_sizer) return Type::LEFT;
|
||||
|
||||
if (right_sizer && top_sizer) return Type::TOP_RIGHT;
|
||||
if (right_sizer && bottom_sizer) return Type::BOTTOM_RIGHT;
|
||||
if (right_sizer) return Type::RIGHT;
|
||||
|
||||
if (top_sizer) return Type::TOP;
|
||||
if (bottom_sizer) return Type::BOTTOM;
|
||||
|
||||
if (hover_window_xml.has_sub_node("title")) return Type::TITLE;
|
||||
if (hover_window_xml.has_sub_node("closer")) return Type::CLOSER;
|
||||
if (hover_window_xml.has_sub_node("maximizer")) return Type::MAXIMIZER;
|
||||
if (hover_window_xml.has_sub_node("minimizer")) return Type::MINIMIZER;
|
||||
|
||||
return Type::UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
void Window_layouter::Main::_handle_hover()
|
||||
{
|
||||
_hover.update();
|
||||
|
||||
User_state::Hover_state const orig_hover_state = _user_state.hover_state();
|
||||
|
||||
try {
|
||||
Xml_node const hover_window_xml = _hover.xml().sub_node("window");
|
||||
|
||||
_user_state.hover({ hover_window_xml.attribute_value("id", 0U) },
|
||||
_element_from_hover_model(hover_window_xml));
|
||||
}
|
||||
|
||||
/*
|
||||
* An exception may occur during the 'Xml_node' construction if the hover
|
||||
* model lacks a window. Under this condition, we invalidate the hover
|
||||
* state.
|
||||
*/
|
||||
catch (...) {
|
||||
|
||||
_user_state.reset_hover();
|
||||
|
||||
/*
|
||||
* Don't generate a focus-model update here. In a situation where the
|
||||
* pointer has moved over a native GUI view (outside the realm of
|
||||
* the window manager), the hover model as generated by the decorator
|
||||
* naturally becomes empty. If we posted a focus update, this would
|
||||
* steal the focus away from the native GUI view.
|
||||
*/
|
||||
}
|
||||
_hover.xml().with_sub_node("window",
|
||||
[&] (Xml_node const &hover) {
|
||||
_user_state.hover({ hover.attribute_value("id", 0U) },
|
||||
Window::Element::from_xml(hover));
|
||||
},
|
||||
[&] /* the hover model lacks a window */ {
|
||||
_user_state.reset_hover();
|
||||
});
|
||||
|
||||
/*
|
||||
* Propagate changed hovering to the decorator
|
||||
@ -698,7 +646,7 @@ void Window_layouter::Main::_handle_focus_request()
|
||||
{
|
||||
_focus_request.update();
|
||||
|
||||
int const id = (int)_focus_request.xml().attribute_value("id", 0L);
|
||||
int const id = _focus_request.xml().attribute_value("id", 0);
|
||||
|
||||
/* don't apply the same focus request twice */
|
||||
if (id == _handled_focus_request_id)
|
||||
@ -718,9 +666,9 @@ void Window_layouter::Main::_handle_focus_request()
|
||||
|
||||
_window_list.for_each_window([&] (Window &window) {
|
||||
|
||||
if (label_matches(prefix, window.label())) {
|
||||
if (label_matches(prefix, window.label)) {
|
||||
window.to_front_cnt(next_to_front_cnt);
|
||||
_user_state.focused_window_id(window.id());
|
||||
_user_state.focused_window_id(window.id);
|
||||
stacking_order_changed = true;
|
||||
}
|
||||
});
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* \brief Window layouter
|
||||
* \author Norman Feske
|
||||
* \date 2015-12-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _OPERATIONS_H_
|
||||
#define _OPERATIONS_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/interface.h>
|
||||
|
||||
/* local includes */
|
||||
#include "window.h"
|
||||
|
||||
namespace Window_layouter { struct Operations; }
|
||||
|
||||
|
||||
struct Window_layouter::Operations : Interface
|
||||
{
|
||||
virtual void close(Window_id) = 0;
|
||||
virtual void toggle_fullscreen(Window_id) = 0;
|
||||
virtual void focus(Window_id) = 0;
|
||||
virtual void release_grab() = 0;
|
||||
virtual void to_front(Window_id) = 0;
|
||||
virtual void drag(Window_id, Window::Element, Point clicked, Point curr) = 0;
|
||||
virtual void finalize_drag(Window_id, Window::Element, Point clicked, Point final) = 0;
|
||||
virtual void screen(Target::Name const &) = 0;
|
||||
};
|
||||
|
||||
#endif /* _OPERATIONS_H_ */
|
@ -20,38 +20,27 @@
|
||||
namespace Window_layouter { class Target; }
|
||||
|
||||
|
||||
class Window_layouter::Target : Noncopyable
|
||||
struct Window_layouter::Target : Noncopyable
|
||||
{
|
||||
public:
|
||||
using Name = String<64>;
|
||||
|
||||
using Name = String<64>;
|
||||
struct Visible { bool value; };
|
||||
|
||||
struct Visible { bool value; };
|
||||
Name const name;
|
||||
unsigned const layer;
|
||||
Rect const rect;
|
||||
bool const visible;
|
||||
|
||||
private:
|
||||
Target(Xml_node target, Rect rect, Visible visible)
|
||||
:
|
||||
name (target.attribute_value("name", Name())),
|
||||
layer(target.attribute_value("layer", 9999U)),
|
||||
rect (rect),
|
||||
visible(visible.value)
|
||||
{ }
|
||||
|
||||
Name const _name;
|
||||
unsigned const _layer;
|
||||
Rect const _geometry;
|
||||
bool const _visible;
|
||||
|
||||
public:
|
||||
|
||||
Target(Xml_node target, Rect geometry, Visible visible)
|
||||
:
|
||||
_name (target.attribute_value("name", Name())),
|
||||
_layer(target.attribute_value("layer", 9999U)),
|
||||
_geometry(geometry),
|
||||
_visible(visible.value)
|
||||
{ }
|
||||
|
||||
/* needed to use class as 'Registered<Target>' */
|
||||
virtual ~Target() { }
|
||||
|
||||
Name name() const { return _name; }
|
||||
unsigned layer() const { return _layer; }
|
||||
Rect geometry() const { return _geometry; }
|
||||
bool visible() const { return _visible; }
|
||||
/* needed to use class as 'Registered<Target>' */
|
||||
virtual ~Target() { }
|
||||
};
|
||||
|
||||
#endif /* _TARGET_H_ */
|
||||
|
@ -144,8 +144,8 @@ class Window_layouter::Target_list
|
||||
/* search targets for next matching layer */
|
||||
unsigned layer = MAX_LAYER;
|
||||
_targets.for_each([&] (Target const &target) {
|
||||
if (target.layer() >= min_layer && target.layer() <= layer)
|
||||
layer = target.layer(); });
|
||||
if (target.layer >= min_layer && target.layer <= layer)
|
||||
layer = target.layer; });
|
||||
|
||||
Rect const drag_origin_boundary = drag.dragging() && drag.moving
|
||||
? target_boundary(assignments, drag.window_id)
|
||||
@ -153,31 +153,31 @@ class Window_layouter::Target_list
|
||||
/* search target by name */
|
||||
_targets.for_each([&] (Target const &target) {
|
||||
|
||||
if (target.layer() != layer)
|
||||
if (target.layer != layer)
|
||||
return;
|
||||
|
||||
if (!target.visible())
|
||||
if (!target.visible)
|
||||
return;
|
||||
|
||||
if (assignments.target_empty(target.name()) && !drag.moving_at_target_rect(target.geometry()))
|
||||
if (assignments.target_empty(target.name) && !drag.moving_at_target_rect(target.rect))
|
||||
return;
|
||||
|
||||
Rect const boundary = target.geometry();
|
||||
Rect const boundary = target.rect;
|
||||
xml.node("boundary", [&] {
|
||||
xml.attribute("name", target.name());
|
||||
xml.attribute("name", target.name);
|
||||
generate(xml, boundary);
|
||||
|
||||
/* in-flux window node for the currently dragged window */
|
||||
if (drag.moving_at_target_rect(target.geometry()))
|
||||
if (drag.moving_at_target_rect(target.rect))
|
||||
assignments.for_each([&] (Assign const &assign) {
|
||||
assign.for_each_member([&] (Assign::Member const &member) {
|
||||
if (drag.moving_window(member.window.id()))
|
||||
if (drag.moving_window(member.window.id))
|
||||
member.window.generate(xml, drag_origin_boundary); }); });
|
||||
|
||||
/* visit all windows on the layer, except for the dragged one */
|
||||
assignments.for_each_visible(target.name(), [&] (Assign const &assign) {
|
||||
assignments.for_each_visible(target.name, [&] (Assign const &assign) {
|
||||
assign.for_each_member([&] (Assign::Member const &member) {
|
||||
if (!drag.moving_window(member.window.id()))
|
||||
if (!drag.moving_window(member.window.id))
|
||||
member.window.generate(xml, boundary); }); });
|
||||
});
|
||||
});
|
||||
@ -291,7 +291,7 @@ class Window_layouter::Target_list
|
||||
{
|
||||
Target const *ptr = nullptr;
|
||||
for_each([&] (Target const &target) {
|
||||
if (target.name() == name)
|
||||
if (target.name == name)
|
||||
ptr = ⌖ });
|
||||
if (ptr)
|
||||
fn(*ptr);
|
||||
@ -301,7 +301,7 @@ class Window_layouter::Target_list
|
||||
{
|
||||
Target const *ptr = nullptr;
|
||||
for_each([&] (Target const &target) {
|
||||
if (target.visible() && target.geometry().contains(at))
|
||||
if (target.visible && target.rect.contains(at))
|
||||
ptr = ⌖ });
|
||||
if (ptr)
|
||||
fn(*ptr);
|
||||
@ -311,9 +311,9 @@ class Window_layouter::Target_list
|
||||
{
|
||||
Target const *ptr = nullptr;
|
||||
_targets.for_each([&] (Target const &target) {
|
||||
assignments.for_each_visible(target.name(), [&] (Assign const &assign) {
|
||||
assignments.for_each_visible(target.name, [&] (Assign const &assign) {
|
||||
assign.for_each_member([&] (Assign::Member const &member) {
|
||||
if (member.window.id() == id)
|
||||
if (member.window.id == id)
|
||||
ptr = ⌖ }); }); });
|
||||
if (ptr)
|
||||
fn(*ptr);
|
||||
@ -326,10 +326,9 @@ class Window_layouter::Target_list
|
||||
{
|
||||
Rect result { };
|
||||
with_target(assignments, id, [&] (Target const &target) {
|
||||
result = target.geometry(); });
|
||||
result = target.rect; });
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif /* _TARGET_LIST_H_ */
|
||||
|
@ -15,8 +15,7 @@
|
||||
#define _USER_STATE_H_
|
||||
|
||||
/* local includes */
|
||||
#include "operations.h"
|
||||
#include "key_sequence_tracker.h"
|
||||
#include <key_sequence_tracker.h>
|
||||
|
||||
namespace Window_layouter { class User_state; }
|
||||
|
||||
@ -25,15 +24,22 @@ class Window_layouter::User_state
|
||||
{
|
||||
public:
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void close(Window_id) = 0;
|
||||
virtual void toggle_fullscreen(Window_id) = 0;
|
||||
virtual void focus(Window_id) = 0;
|
||||
virtual void release_grab() = 0;
|
||||
virtual void to_front(Window_id) = 0;
|
||||
virtual void drag(Window_id, Window::Element, Point clicked, Point curr) = 0;
|
||||
virtual void finalize_drag(Window_id, Window::Element, Point clicked, Point final) = 0;
|
||||
virtual void screen(Target::Name const &) = 0;
|
||||
};
|
||||
|
||||
struct Hover_state
|
||||
{
|
||||
Window_id window_id;
|
||||
Window::Element element { Window::Element::UNDEFINED };
|
||||
|
||||
Hover_state(Window_id id, Window::Element element)
|
||||
:
|
||||
window_id(id), element(element)
|
||||
{ }
|
||||
Window::Element element;
|
||||
|
||||
bool operator != (Hover_state const &other) const
|
||||
{
|
||||
@ -44,16 +50,18 @@ class Window_layouter::User_state
|
||||
|
||||
private:
|
||||
|
||||
Action &_action;
|
||||
|
||||
Window_id _hovered_window_id { };
|
||||
Window_id _focused_window_id { };
|
||||
Window_id _dragged_window_id { };
|
||||
|
||||
unsigned _key_cnt = 0;
|
||||
unsigned _key_cnt = 0;
|
||||
|
||||
Key_sequence_tracker _key_sequence_tracker { };
|
||||
|
||||
Window::Element _hovered_element = Window::Element::UNDEFINED;
|
||||
Window::Element _dragged_element = Window::Element::UNDEFINED;
|
||||
Window::Element _hovered_element { };
|
||||
Window::Element _dragged_element { };
|
||||
|
||||
/*
|
||||
* True while drag operation in progress
|
||||
@ -77,8 +85,6 @@ class Window_layouter::User_state
|
||||
*/
|
||||
Point _pointer_curr { };
|
||||
|
||||
Operations &_operations;
|
||||
|
||||
Focus_history &_focus_history;
|
||||
|
||||
/*
|
||||
@ -107,16 +113,16 @@ class Window_layouter::User_state
|
||||
/*
|
||||
* Toggle maximized (fullscreen) state
|
||||
*/
|
||||
if (_hovered_element == Window::Element::MAXIMIZER) {
|
||||
if (_hovered_element.maximizer()) {
|
||||
|
||||
_dragged_window_id = _hovered_window_id;
|
||||
_focused_window_id = _hovered_window_id;
|
||||
_focus_history.focus(_focused_window_id);
|
||||
|
||||
_operations.toggle_fullscreen(_hovered_window_id);
|
||||
_action.toggle_fullscreen(_hovered_window_id);
|
||||
|
||||
_hovered_element = Window::Element::UNDEFINED;
|
||||
_hovered_window_id = Window_id();
|
||||
_hovered_element = { };
|
||||
_hovered_window_id = { };
|
||||
return;
|
||||
}
|
||||
|
||||
@ -128,19 +134,19 @@ class Window_layouter::User_state
|
||||
_focused_window_id = _hovered_window_id;
|
||||
_focus_history.focus(_focused_window_id);
|
||||
|
||||
_operations.to_front(_hovered_window_id);
|
||||
_operations.focus(_hovered_window_id);
|
||||
_action.to_front(_hovered_window_id);
|
||||
_action.focus(_hovered_window_id);
|
||||
}
|
||||
|
||||
_operations.drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
_action.drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
User_state(Operations &operations, Focus_history &focus_history)
|
||||
User_state(Action &action, Focus_history &focus_history)
|
||||
:
|
||||
_operations(operations), _focus_history(focus_history)
|
||||
_action(action), _focus_history(focus_history)
|
||||
{ }
|
||||
|
||||
void handle_input(Input::Event const events[], unsigned num_events,
|
||||
@ -155,8 +161,8 @@ class Window_layouter::User_state
|
||||
* Issue drag operation when in dragged state
|
||||
*/
|
||||
if (_drag_state && _drag_init_done && (_pointer_curr != pointer_last))
|
||||
_operations.drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
_action.drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
}
|
||||
|
||||
void hover(Window_id window_id, Window::Element element)
|
||||
@ -186,15 +192,13 @@ class Window_layouter::User_state
|
||||
|
||||
/*
|
||||
* Let focus follows the pointer
|
||||
*
|
||||
* XXX obtain policy from config
|
||||
*/
|
||||
if (!_drag_state && _hovered_window_id.valid()
|
||||
&& _hovered_window_id != last_hovered_window_id) {
|
||||
|
||||
_focused_window_id = _hovered_window_id;
|
||||
_focus_history.focus(_focused_window_id);
|
||||
_operations.focus(_focused_window_id);
|
||||
_action.focus(_focused_window_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,8 +208,8 @@ class Window_layouter::User_state
|
||||
if (_drag_state)
|
||||
return;
|
||||
|
||||
_hovered_element = Window::Element::UNDEFINED;
|
||||
_hovered_window_id = Window_id();
|
||||
_hovered_element = { };
|
||||
_hovered_window_id = { };
|
||||
}
|
||||
|
||||
Window_id focused_window_id() const { return _focused_window_id; }
|
||||
@ -225,8 +229,8 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
if (e.absolute_motion() || e.focus_enter()) {
|
||||
|
||||
if (_drag_state && _drag_init_done)
|
||||
_operations.drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
_action.drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
}
|
||||
|
||||
/* track number of pressed buttons/keys */
|
||||
@ -277,12 +281,12 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
/*
|
||||
* Issue resize to 0x0 when releasing the the window closer
|
||||
*/
|
||||
if (_dragged_element == Window::Element::CLOSER)
|
||||
if (_dragged_element.closer())
|
||||
if (_dragged_element == _hovered_element)
|
||||
_operations.close(_dragged_window_id);
|
||||
_action.close(_dragged_window_id);
|
||||
|
||||
_operations.finalize_drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
_action.finalize_drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,38 +296,38 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
if (e.press() && _key_cnt == 1)
|
||||
_key_sequence_tracker.reset();
|
||||
|
||||
_key_sequence_tracker.apply(e, config, [&] (Action action) {
|
||||
_key_sequence_tracker.apply(e, config, [&] (Command const &command) {
|
||||
|
||||
switch (action.type()) {
|
||||
switch (command.type) {
|
||||
|
||||
case Action::TOGGLE_FULLSCREEN:
|
||||
_operations.toggle_fullscreen(_focused_window_id);
|
||||
case Command::TOGGLE_FULLSCREEN:
|
||||
_action.toggle_fullscreen(_focused_window_id);
|
||||
return;
|
||||
|
||||
case Action::RAISE_WINDOW:
|
||||
_operations.to_front(_focused_window_id);
|
||||
case Command::RAISE_WINDOW:
|
||||
_action.to_front(_focused_window_id);
|
||||
return;
|
||||
|
||||
case Action::NEXT_WINDOW:
|
||||
case Command::NEXT_WINDOW:
|
||||
_focused_window_id = _focus_history.next(_focused_window_id);
|
||||
_operations.focus(_focused_window_id);
|
||||
_action.focus(_focused_window_id);
|
||||
return;
|
||||
|
||||
case Action::PREV_WINDOW:
|
||||
case Command::PREV_WINDOW:
|
||||
_focused_window_id = _focus_history.prev(_focused_window_id);
|
||||
_operations.focus(_focused_window_id);
|
||||
_action.focus(_focused_window_id);
|
||||
return;
|
||||
|
||||
case Action::SCREEN:
|
||||
_operations.screen(action.target_name());
|
||||
case Command::SCREEN:
|
||||
_action.screen(command.target);
|
||||
return;
|
||||
|
||||
case Action::RELEASE_GRAB:
|
||||
_operations.release_grab();
|
||||
case Command::RELEASE_GRAB:
|
||||
_action.release_grab();
|
||||
return;
|
||||
|
||||
default:
|
||||
warning("action ", (int)action.type(), " unhanded");
|
||||
warning("command ", (int)command.type, " unhanded");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -58,7 +58,29 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
return "";
|
||||
}
|
||||
|
||||
Element(Type type) : type(type) { }
|
||||
static Element from_xml(Xml_node const &hover)
|
||||
{
|
||||
bool const left = hover.has_sub_node("left_sizer"),
|
||||
right = hover.has_sub_node("right_sizer"),
|
||||
top = hover.has_sub_node("top_sizer"),
|
||||
bottom = hover.has_sub_node("bottom_sizer");
|
||||
|
||||
if (top && left) return { TOP_LEFT };
|
||||
if (bottom && left) return { BOTTOM_LEFT };
|
||||
if (left) return { LEFT };
|
||||
if (top && right) return { TOP_RIGHT };
|
||||
if (bottom && right) return { BOTTOM_RIGHT };
|
||||
if (right) return { RIGHT };
|
||||
if (top) return { TOP };
|
||||
if (bottom) return { BOTTOM };
|
||||
|
||||
if (hover.has_sub_node("title")) return { TITLE };
|
||||
if (hover.has_sub_node("closer")) return { CLOSER };
|
||||
if (hover.has_sub_node("maximizer")) return { MAXIMIZER };
|
||||
if (hover.has_sub_node("minimizer")) return { MINIMIZER };
|
||||
|
||||
return { UNDEFINED };
|
||||
}
|
||||
|
||||
bool operator != (Element const &other) const { return other.type != type; }
|
||||
bool operator == (Element const &other) const { return other.type == type; }
|
||||
@ -71,16 +93,29 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|| type == BOTTOM_LEFT || type == BOTTOM_RIGHT
|
||||
|| type == MAXIMIZER;
|
||||
}
|
||||
|
||||
bool _any_of(Type t1, Type t2, Type t3) const
|
||||
{
|
||||
return type == t1 || type == t2 || type == t3;
|
||||
}
|
||||
|
||||
bool left() const { return _any_of(LEFT, TOP_LEFT, BOTTOM_LEFT); }
|
||||
bool right() const { return _any_of(RIGHT, TOP_RIGHT, BOTTOM_RIGHT); }
|
||||
bool top() const { return _any_of(TOP, TOP_LEFT, TOP_RIGHT); }
|
||||
bool bottom() const { return _any_of(BOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT); }
|
||||
|
||||
bool maximizer() const { return type == MAXIMIZER; }
|
||||
bool closer() const { return type == CLOSER; }
|
||||
};
|
||||
|
||||
Window_id const id;
|
||||
|
||||
Label const label;
|
||||
|
||||
private:
|
||||
|
||||
Window_id const _id;
|
||||
|
||||
Title _title { };
|
||||
|
||||
Label const _label;
|
||||
|
||||
Decorator_margins const &_decorator_margins;
|
||||
|
||||
Rect _geometry { };
|
||||
@ -172,10 +207,13 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|
||||
Focus_history::Entry _focus_history_entry;
|
||||
|
||||
bool _drag_left_border = false;
|
||||
bool _drag_right_border = false;
|
||||
bool _drag_top_border = false;
|
||||
bool _drag_bottom_border = false;
|
||||
struct Dragged_border
|
||||
{
|
||||
bool left, right, top, bottom;
|
||||
|
||||
bool any() const { return left || right || top || bottom; };
|
||||
|
||||
} _dragged_border { };
|
||||
|
||||
/**
|
||||
* Called when the user starts dragging a window element
|
||||
@ -185,22 +223,10 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
_dragged_element = element;
|
||||
|
||||
if (_resizeable) {
|
||||
|
||||
_drag_left_border = (element.type == Window::Element::LEFT)
|
||||
|| (element.type == Window::Element::TOP_LEFT)
|
||||
|| (element.type == Window::Element::BOTTOM_LEFT);
|
||||
|
||||
_drag_right_border = (element.type == Window::Element::RIGHT)
|
||||
|| (element.type == Window::Element::TOP_RIGHT)
|
||||
|| (element.type == Window::Element::BOTTOM_RIGHT);
|
||||
|
||||
_drag_top_border = (element.type == Window::Element::TOP)
|
||||
|| (element.type == Window::Element::TOP_LEFT)
|
||||
|| (element.type == Window::Element::TOP_RIGHT);
|
||||
|
||||
_drag_bottom_border = (element.type == Window::Element::BOTTOM)
|
||||
|| (element.type == Window::Element::BOTTOM_LEFT)
|
||||
|| (element.type == Window::Element::BOTTOM_RIGHT);
|
||||
_dragged_border.left = element.left();
|
||||
_dragged_border.right = element.right();
|
||||
_dragged_border.top = element.top();
|
||||
_dragged_border.bottom = element.bottom();
|
||||
}
|
||||
|
||||
_orig_geometry = _geometry;
|
||||
@ -217,7 +243,7 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
void _apply_drag_operation(Point offset)
|
||||
{
|
||||
/* move window */
|
||||
if (!_drag_border()) {
|
||||
if (!_dragged_border.any()) {
|
||||
_drag_geometry = Rect(_orig_geometry.p1() + offset,
|
||||
_orig_geometry.area);
|
||||
return;
|
||||
@ -233,25 +259,16 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|
||||
auto clamped = [] (int v, int lowest, int highest) { return min(max(v, lowest), highest); };
|
||||
|
||||
if (_drag_left_border) x1 = clamped(min(x1 + offset.x, x2), inner.x1(), outer.x2());
|
||||
if (_drag_right_border) x2 = clamped(max(x2 + offset.x, x1), outer.x1(), inner.x2());
|
||||
if (_drag_top_border) y1 = clamped(min(y1 + offset.y, y2), inner.y1(), outer.y2());
|
||||
if (_drag_bottom_border) y2 = clamped(max(y2 + offset.y, y1), outer.y1(), inner.y2());
|
||||
if (_dragged_border.left) x1 = clamped(min(x1 + offset.x, x2), inner.x1(), outer.x2());
|
||||
if (_dragged_border.right) x2 = clamped(max(x2 + offset.x, x1), outer.x1(), inner.x2());
|
||||
if (_dragged_border.top) y1 = clamped(min(y1 + offset.y, y2), inner.y1(), outer.y2());
|
||||
if (_dragged_border.bottom) y2 = clamped(max(y2 + offset.y, y1), outer.y1(), inner.y2());
|
||||
|
||||
_drag_geometry = Rect::compound(Point { x1, y1 }, Point { x2, y2 });
|
||||
|
||||
_dragged_size = _drag_geometry.area;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if user drags a window border
|
||||
*/
|
||||
bool _drag_border() const
|
||||
{
|
||||
return _drag_left_border || _drag_right_border
|
||||
|| _drag_top_border || _drag_bottom_border;
|
||||
}
|
||||
|
||||
Constructible<Assign::Member> _assign_member { };
|
||||
|
||||
public:
|
||||
@ -260,21 +277,15 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
Focus_history &focus_history,
|
||||
Decorator_margins const &decorator_margins)
|
||||
:
|
||||
_id(id), _label(label),
|
||||
id(id), label(label),
|
||||
_decorator_margins(decorator_margins),
|
||||
_client_size(initial_size),
|
||||
_dragged_size(initial_size),
|
||||
_focus_history_entry(focus_history, _id)
|
||||
_focus_history_entry(focus_history, id)
|
||||
{ }
|
||||
|
||||
bool has_id(Window_id id) const { return id == _id; }
|
||||
|
||||
Window_id id() const { return _id; }
|
||||
|
||||
void title(Title const &title) { _title = title; }
|
||||
|
||||
Label label() const { return _label; }
|
||||
|
||||
bool dragged() const { return _dragged; }
|
||||
|
||||
Rect effective_inner_geometry() const
|
||||
@ -286,14 +297,14 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
x2 = _orig_geometry.x2(), y2 = _orig_geometry.y2();
|
||||
|
||||
/* move window */
|
||||
if (!_drag_border())
|
||||
if (!_dragged_border.any())
|
||||
return _drag_geometry;
|
||||
|
||||
/* resize window */
|
||||
if (_drag_left_border) x1 = x2 - _client_size.w + 1;
|
||||
if (_drag_right_border) x2 = x1 + _client_size.w - 1;
|
||||
if (_drag_top_border) y1 = y2 - _client_size.h + 1;
|
||||
if (_drag_bottom_border) y2 = y1 + _client_size.h - 1;
|
||||
if (_dragged_border.left) x1 = x2 - _client_size.w + 1;
|
||||
if (_dragged_border.right) x2 = x1 + _client_size.w - 1;
|
||||
if (_dragged_border.top) y1 = y2 - _client_size.h + 1;
|
||||
if (_dragged_border.bottom) y2 = y1 + _client_size.h - 1;
|
||||
|
||||
return Rect::compound(Point(x1, y1), Point(x2, y2));
|
||||
}
|
||||
@ -335,8 +346,6 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|
||||
bool resizeable() const { return _resizeable; }
|
||||
|
||||
bool label_matches(Label const &label) const { return label == _label; }
|
||||
|
||||
/**
|
||||
* Define window size
|
||||
*
|
||||
@ -370,7 +379,7 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
return;
|
||||
|
||||
xml.node("window", [&] () {
|
||||
xml.attribute("id", _id.value);
|
||||
xml.attribute("id", id.value);
|
||||
xml.attribute("width", size.w);
|
||||
xml.attribute("height", size.h);
|
||||
});
|
||||
@ -384,14 +393,14 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|
||||
xml.node("window", [&]() {
|
||||
|
||||
xml.attribute("id", _id.value);
|
||||
xml.attribute("id", id.value);
|
||||
|
||||
/* present concatenation of label and title in the window's title bar */
|
||||
{
|
||||
bool const has_title = strlen(_title.string()) > 0;
|
||||
|
||||
String<Label::capacity()> const
|
||||
title(_label, (has_title ? " " : ""), _title);
|
||||
title(label, (has_title ? " " : ""), _title);
|
||||
|
||||
xml.attribute("title", title);
|
||||
}
|
||||
@ -471,13 +480,10 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|
||||
void finalize_drag_operation()
|
||||
{
|
||||
_drag_left_border = false;
|
||||
_drag_right_border = false;
|
||||
_drag_top_border = false;
|
||||
_drag_bottom_border = false;
|
||||
_geometry = effective_inner_geometry();
|
||||
_dragged_size = _geometry.area;
|
||||
_dragged = false;
|
||||
_dragged_border = { };
|
||||
_geometry = effective_inner_geometry();
|
||||
_dragged_size = _geometry.area;
|
||||
_dragged = false;
|
||||
}
|
||||
|
||||
void to_front_cnt(unsigned to_front_cnt) { _to_front_cnt = to_front_cnt; }
|
||||
@ -520,7 +526,7 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
*/
|
||||
bool matches(Xml_node const &node) const
|
||||
{
|
||||
return node.attribute_value("id", 0U) == _id.value;
|
||||
return node.attribute_value("id", 0U) == id.value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ class Window_layouter::Window_list
|
||||
{
|
||||
public:
|
||||
|
||||
struct Change_handler : Interface
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void window_list_changed() = 0;
|
||||
};
|
||||
@ -34,7 +34,7 @@ class Window_layouter::Window_list
|
||||
|
||||
Env &_env;
|
||||
Allocator &_alloc;
|
||||
Change_handler &_change_handler;
|
||||
Action &_action;
|
||||
Focus_history &_focus_history;
|
||||
Decorator_margins const &_decorator_margins;
|
||||
|
||||
@ -79,20 +79,20 @@ class Window_layouter::Window_list
|
||||
);
|
||||
|
||||
/* notify main program */
|
||||
_change_handler.window_list_changed();
|
||||
_action.window_list_changed();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Window_list(Env &env,
|
||||
Allocator &alloc,
|
||||
Change_handler &change_handler,
|
||||
Action &action,
|
||||
Focus_history &focus_history,
|
||||
Decorator_margins const &decorator_margins)
|
||||
:
|
||||
_env(env),
|
||||
_alloc(alloc),
|
||||
_change_handler(change_handler),
|
||||
_action(action),
|
||||
_focus_history(focus_history),
|
||||
_decorator_margins(decorator_margins)
|
||||
{
|
||||
@ -107,27 +107,22 @@ class Window_layouter::Window_list
|
||||
win.dissolve_from_assignment(); });
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void with_window(Window_id id, FN const &fn)
|
||||
void with_window(Window_id id, auto const &fn)
|
||||
{
|
||||
_list.for_each([&] (Window &win) {
|
||||
if (win.has_id(id))
|
||||
if (win.id == id)
|
||||
fn(win); });
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void with_window(Window_id id, FN const &fn) const
|
||||
void with_window(Window_id id, auto const &fn) const
|
||||
{
|
||||
_list.for_each([&] (Window const &win) {
|
||||
if (win.has_id(id))
|
||||
if (win.id == id)
|
||||
fn(win); });
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void for_each_window(FN const &fn) { _list.for_each(fn); }
|
||||
|
||||
template <typename FN>
|
||||
void for_each_window(FN const &fn) const { _list.for_each(fn); }
|
||||
void for_each_window(auto const &fn) { _list.for_each(fn); }
|
||||
void for_each_window(auto const &fn) const { _list.for_each(fn); }
|
||||
};
|
||||
|
||||
#endif /* _WINDOW_LIST_H_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user