Skip to content

Commit

Permalink
Add wf::maximization_t.
Browse files Browse the repository at this point in the history
TILED_EDGES_* are now stored in src/api/wayfire/maximization.hpp
class maximization_t is a wrapper around a unsigned char, representing
two booleans (as bits): maximized vertically and/or horizontally.

toplevel_state_t, which stores tiled_edges that still had to
fully represent the maximization state, has been given operators
and accessors to seamlessly work with maximization_t types from
the outside, while only storing a uint32_t tiled_edges internally.

expand_geometry_by_margins and shrink_geometry_by_margins can now
be used conditionally - they are basically the same as
expand_geometry_if, but kept for legacy (API) reasons.

Then the hard part: everywhere where before `tiled_edges` was
used as a boolean, where zero meant not-maximized, and non-zero
meant fully-maximized, this commit either fixes that code to deal
properly with unidirectional maximization states, or adds a TODO
comment that this still has to be added. I don't think I missed
a place, despite the fact that the access to tiled_edges isn't
encapsulated.
  • Loading branch information
CarloWood committed Mar 2, 2025
1 parent 17cd5b3 commit 756fc60
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 35 deletions.
206 changes: 206 additions & 0 deletions src/api/wayfire/maximization.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#pragma once

#include <wlr/util/edges.h>
#include <cstdint>

class wayfire_resize;
class wayfire_decoration;
namespace wf
{
namespace grid
{
class grid_animation_t;
}

namespace tile
{
class view_node_t;
}

/**
* A bitmask consisting of the top and bottom edges.
* This corresponds to a vertically maximized state.
*/
constexpr uint32_t TILED_EDGES_VERTICAL =
WLR_EDGE_TOP | WLR_EDGE_BOTTOM;

/**
* A bitmask consisting of the left and right edges.
* This corresponds to a horizontally maximized state.
*/
constexpr uint32_t TILED_EDGES_HORIZONTAL =
WLR_EDGE_LEFT | WLR_EDGE_RIGHT;

/**
* A bitmask consisting of all tiled edges.
* This corresponds to a maximized state.
*/
constexpr uint32_t TILED_EDGES_ALL =
TILED_EDGES_VERTICAL | TILED_EDGES_HORIZONTAL;

/**
* Represents the maximization state (like in pending), not toggle.
*
* We can no longer use a boolean for "maximized" or not.
* This class wraps two bits to represent being maximized vertically, horizontally or both.
*
* To test for vertical maximization (including full maximization), use:
*
* maximization >= maximization_t::vertical
*
* To test if the maximization state is purely vertical and not horizontal, use:
*
* maximization == maximization_t::vertical
*
* Likewise for horizontal.
*/
class maximization_t
{
public:
struct state_t
{
using mask_t = unsigned char;
mask_t mask_;

constexpr state_t operator |(state_t state) const
{
return {static_cast<mask_t>(mask_ | state.mask_)};
}

constexpr state_t operator &(state_t state) const
{
return {static_cast<mask_t>(mask_ & state.mask_)};
}

bool operator ==(state_t state) const
{
return mask_ == state.mask_;
}

bool operator !=(state_t state) const
{
return mask_ != state.mask_;
}
};

static constexpr state_t none{0};
static constexpr state_t vertical{1};
static constexpr state_t horizontal{2};
static constexpr state_t full{vertical.mask_ | horizontal.mask_};

private:
state_t state_;

/**
* Convert a tiled_edges bit mask to a maximization_t.
*/
friend struct toplevel_state_t;
maximization_t(uint32_t tiled_edges) : state_{
((tiled_edges & TILED_EDGES_VERTICAL) == TILED_EDGES_VERTICAL ? vertical : none) |
((tiled_edges & TILED_EDGES_HORIZONTAL) == TILED_EDGES_HORIZONTAL ? horizontal : none)}
{}

public:
/**
* Default constructor is a non-maximized state.
*/
maximization_t() : state_{none}
{}

/**
* Allow users to use maximization_t::full etc. where a maximization_t is required.
*/
maximization_t(state_t state) : state_(state)
{}

/**
* Add two states together.
*
* This is akin to a bit-wise OR.
*/
maximization_t& operator +=(maximization_t maximization)
{
state_.mask_ |= maximization.state_.mask_;
return *this;
}

/**
* Remove maximization state.
*
* Clear the bits that are set in `state`.
*/
maximization_t& operator -=(maximization_t maximization)
{
state_.mask_ &= ~maximization.state_.mask_;
return *this;
}

/**
* Toggle maximization state.
*/
maximization_t& operator ^=(maximization_t maximization)
{
state_.mask_ ^= maximization.state_.mask_;
return *this;
}

/**
* Invert maximization state.
*/
maximization_t operator ~() const
{
maximization_t inverted{*this};
inverted ^= full;
return inverted;
}

/**
* Test contains maximization state.
*
* Returns true if the bits set in `state` are also set in this object.
*/
bool operator >=(maximization_t maximization) const
{
return (state_ & maximization.state_) == maximization.state_;
}

/**
* Test does not contain maximization state.
*
* Returns true if the bits set in `state` are not set in this object.
*/
bool operator <(maximization_t maximization) const
{
return (state_ & maximization.state_) == none;
}

/**
* Test equal.
*
* Returns true if the maximization states are the same.
*/
bool operator ==(maximization_t maximization) const
{
return state_ == maximization.state_;
}

/**
* Test unequal.
*
* Returns true if the maximization states are not the same.
*/
bool operator !=(maximization_t maximization) const
{
return state_ != maximization.state_;
}

/**
* Convert maximization_t to tiled_edges.
*/
uint32_t as_tiled_edges() const
{
return ((state_ & vertical) !=
none ? TILED_EDGES_VERTICAL : 0) | ((state_ & horizontal) != none ? TILED_EDGES_HORIZONTAL : 0);
}
};
} // namespace wf
12 changes: 5 additions & 7 deletions src/api/wayfire/toplevel-view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@ enum view_allowed_actions_t
VIEW_ALLOW_ALL = VIEW_ALLOW_MOVE | VIEW_ALLOW_RESIZE | VIEW_ALLOW_WS_CHANGE,
};

/**
* A bitmask consisting of all tiled edges.
* This corresponds to a maximized state.
*/
constexpr uint32_t TILED_EDGES_ALL =
WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT;

/**
* Toplevel views are a subtype of views which have an associated toplevel object. As such, they may be moved,
* resized, etc. freely by plugins and have many additional operations when compared to other view types.
Expand Down Expand Up @@ -124,6 +117,11 @@ class toplevel_view_interface_t : public virtual wf::view_interface_t
return toplevel()->pending().tiled_edges;
}

inline maximization_t pending_maximization() const
{
return toplevel()->pending();
}

inline bool pending_fullscreen() const
{
return toplevel()->pending().fullscreen;
Expand Down
72 changes: 59 additions & 13 deletions src/api/wayfire/toplevel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <wayfire/nonstd/wlroots.hpp>
#include "wayfire/geometry.hpp"
#include "wayfire/object.hpp"
#include "wayfire/maximization.hpp"
#include <wayfire/txn/transaction-object.hpp>
#include "wayfire/nonstd/wlroots.hpp" // IWYU pragma: keep

Expand Down Expand Up @@ -43,7 +44,7 @@ struct toplevel_state_t
* Tiled edges are edges of the toplevel that are aligned to other objects (output edge, other toplevels,
* etc.). Clients usually draw no shadows, rounded corners and similar decorations on tiled edges.
*
* Usually, when all edges are tiled, the toplevel is considered maximized.
* When two opposing edges are tiled, the toplevel is considered maximized in that direction.
*/
uint32_t tiled_edges = 0;

Expand All @@ -62,6 +63,47 @@ struct toplevel_state_t
* finalized before the view is actually committed.
*/
decoration_margins_t margins = {0, 0, 0, 0};

public:
/**
* Extract maximization state from tiled_edges.
*/
operator maximization_t() const
{
return {tiled_edges};
}

/**
* Convert a maximization_t to tiled_edges.
*/
toplevel_state_t& operator =(maximization_t maximization)
{
tiled_edges = maximization.as_tiled_edges();
return *this;
}

/**
* Compare toplevel_state_t object directly with a maximization_t.
*/
bool operator <(maximization_t maximization) const
{
return maximization_t{tiled_edges} < maximization;
}

bool operator >=(maximization_t maximization) const
{
return maximization_t{tiled_edges} >= maximization;
}

bool operator ==(maximization_t maximization) const
{
return maximization_t{tiled_edges} == maximization;
}

bool operator !=(maximization_t maximization) const
{
return maximization_t{tiled_edges} != maximization;
}
};

/**
Expand Down Expand Up @@ -140,21 +182,25 @@ inline wf::dimensions_t shrink_dimensions_by_margins(wf::dimensions_t dim,
return dim;
}

inline wf::geometry_t expand_geometry_by_margins(wf::geometry_t geometry, const decoration_margins_t& margins)
/**
* Expand unless maximization contains the direction.
*
* The default expands both directions.
*/
inline wf::geometry_t expand_geometry_by_margins(wf::geometry_t geometry, const decoration_margins_t& margins,
maximization_t maximization = maximization_t::none)
{
geometry.x -= margins.left;
geometry.y -= margins.top;
geometry.width += margins.left + margins.right;
geometry.height += margins.top + margins.bottom;
return geometry;
return expand_geometry_if(geometry, maximization.as_tiled_edges(), {}, margins);
}

inline wf::geometry_t shrink_geometry_by_margins(wf::geometry_t geometry, const decoration_margins_t& margins)
/**
* Shrink unless maximization contains the direction.
*
* The default shrinks both directions.
*/
inline wf::geometry_t shrink_geometry_by_margins(wf::geometry_t geometry, const decoration_margins_t& margins,
maximization_t maximization = maximization_t::none)
{
geometry.x += margins.left;
geometry.y += margins.top;
geometry.width -= margins.left + margins.right;
geometry.height -= margins.top + margins.bottom;
return geometry;
return expand_geometry_if(geometry, maximization.as_tiled_edges(), {}, -margins);
}
}
18 changes: 13 additions & 5 deletions src/output/workspace-impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,24 @@ struct workspace_set_t::impl
if (view->get_output() && view->toplevel()->current().fullscreen)
{
view->set_geometry(new_geometry);
} else if (view->toplevel()->current().tiled_edges)
} else if (view->toplevel()->current() == maximization_t::full)
{
// Do nothing. This is taken care of, by the grid plugin.
// If the user does not have grid enabled, we ignore it anyways.
} else
{
view->set_geometry({
int(px * new_geometry.width), int(py * new_geometry.height),
wm.width, wm.height
});
wf::geometry_t& new_geometry = wm;
if (view->toplevel()->current() < maximization_t::vertical)
{
new_geometry.y = int(py * new_geometry.height);
}

if (view->toplevel()->current() < maximization_t::horizontal)
{
new_geometry.x = int(px * new_geometry.width);
}

view->set_geometry(new_geometry);
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/view/view-impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,15 +449,18 @@ void wf::adjust_view_pending_geometry_on_start_map(wf::toplevel_view_interface_t
wf::get_core().default_wm->fullscreen_request(self, self->get_output(), true);
} else if (map_maximized)
{
self->toplevel()->pending().tiled_edges = wf::TILED_EDGES_ALL;
// TODO: what about partial maximization?
self->toplevel()->pending() = maximization_t::full;
if (self->get_output())
{
self->toplevel()->pending().geometry = self->get_output()->workarea->get_workarea();
}
} else
{
// TODO: at the moment we are assuming that we're not unidirectional maximized at this point.
// Therefore, not vertically and also not horizontally.
auto map_geometry = wf::expand_geometry_by_margins(map_geometry_client,
self->toplevel()->pending().margins);
self->toplevel()->pending().margins, maximization_t::none);

if (self->get_output())
{
Expand Down
3 changes: 2 additions & 1 deletion src/view/xdg-shell/xdg-toplevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ void wf::xdg_toplevel_t::commit()
auto version = wl_resource_get_version(toplevel->resource);
if (version >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION)
{
// TODO: support unidirectional maximization.
this->target_configure =
wlr_xdg_toplevel_set_maximized(this->toplevel, (_pending.tiled_edges == TILED_EDGES_ALL));
wlr_xdg_toplevel_set_maximized(this->toplevel, _pending == maximization_t::full);
} else
{
this->target_configure = wlr_xdg_toplevel_set_maximized(this->toplevel, !!_pending.tiled_edges);
Expand Down
Loading

0 comments on commit 756fc60

Please sign in to comment.