Compare commits

..

13 Commits

Author SHA1 Message Date
Aire-One 32827126cc Merge remote-tracking branch 'elv/reactive' 2021-03-28 20:09:07 +02:00
Emmanuel Lepage Vallee 830e64bc8d doc: Extend the widget documentation with the new modules.
Adds:

 * gears.watcher
 * gears.reactive
 * gears.connection
2020-11-30 00:43:38 -08:00
Emmanuel Lepage Vallee b28a84fbc3 tests: Test `gears.watcher`. 2020-11-30 00:43:38 -08:00
Emmanuel Lepage Vallee 9d55d385c7 Add a new `gears.watcher` module.
It mostly replaces `awful.widgets.watch` with a more generic API.
`awful.widgets.watch` was only useful for textbox (without abusing
of its low-level callbacks). This can be used both as a generic
polling module for files and commands. It can also be attached
directly to widgets or mixed with `gears.reactive` to build an
higher level output.
2020-11-30 00:43:38 -08:00
Emmanuel Lepage Vallee 2376535e78 tests: Test `gears.connection`. 2020-11-30 00:43:38 -08:00
Emmanuel Lepage Vallee 8f9d23b11b Add a new `gears.connection` module.
This is the declarative DSL version of `:connect_signal`. It also
has some magic to access the widet object using their IDs without
the usual imperative code.
2020-11-30 00:43:38 -08:00
Emmanuel Lepage Vallee 7ffee87528 tests: Test the `gears.reactive` module.
It has both doc and unit tests. They are disabled for Lua 5.1 since
this feature isn't working yet.
2020-11-30 00:43:38 -08:00
Emmanuel Lepage Vallee e6793376e5 Add a new reactive programming module.
This module allows to attach an "expression" to a widget property.
This expression will be evaluated each time it's content may change.

This is the first part of a trio of new APIs to improve setting dynamic
values to a widget from 3 different angles.
2020-11-29 22:33:00 -08:00
Emmanuel Lepage Vallee 537b44b57b widget: Add a generic way to extend the declarative tree system.
With this addition, it is now possible to let the right hand side
part of the `key = value` to handle setting the value.
2020-11-29 03:12:53 -08:00
Emmanuel Lepage Vallee 23d3969d89 timer: Enable the full `gears.object` property support.
In the next few commits, this will be used to implement a replacment
for `awful.widgets.watch`.
2020-11-29 03:12:53 -08:00
Emmanuel Lepage Vallee e3976e0a19 timer: Replace `.data` by `._private`.
First step toward converting it into a normal `gears.object` to
later use a base class.
2020-11-29 03:12:53 -08:00
Emmanuel Lepage Vallee 3feeec3a1f widget.base: Add template support.
This allows some widgets to have declarative templates as first level
elements. It can be used for lazy loading or conditional loading.

This commit also improve get_children_by_id, but does not fix all the
corner case. As proposed by psychon, this will require them to be
moved to the hierarchy.
2020-11-29 03:12:53 -08:00
Emmanuel Lepage Vallee f21c62237e Add the output of some future files.
Otherwise the CI will be angry.
2020-11-29 03:12:53 -08:00
271 changed files with 7472 additions and 12009 deletions

View File

@ -1,156 +0,0 @@
name: Update API docs
on:
# Trigger on push to branches `master` and `3.5`.
push:
branches: [ master, 3.5 ]
# Trigger on pull request events for PRs that have `master` as their target branch
pull_request:
branches: [ master ]
# Allow running the workflow manually
workflow_dispatch:
defaults:
run:
# GitHub Actions adds `errexit` and `pipefail` by default, but we add `xtrace`
# to improve debugging some of the longer scripts.
shell: /bin/bash -o errexit -o pipefail -o xtrace {0}
jobs:
main:
runs-on: ubuntu-20.04
env:
# Used for stable dates in documentation examples. See #2070.
SOURCE_DATE_EPOCH: "1893456000"
steps:
# Create a cache invalidation key based on the current year + week.
# This way, packages will be checked for updates once every week.
- name: Get Date
id: get-date
run: echo "::set-output name=date::$(/bin/date -u "+%Y%W")"
- name: Cache apt packages
id: cache-apt
uses: actions/cache@v2
with:
path: /var/cache/apt/archives
# The trailing number serves as a version flag that can be incremented
# to invalidate the cache after changing the list of packages.
key: ${{ github.workflow }}-${{ runner.os }}-${{ steps.get-date.outputs.date }}-apt-3
- name: Download apt packages
if: steps.cache-apt.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install --download-only -y --no-install-recommends \
asciidoctor \
cmake \
dbus-x11 \
gettext \
gir1.2-gtk-3.0 \
gir1.2-pango-1.0 \
git \
libdbus-1-dev \
libgirepository1.0-dev \
libnotify-bin \
libpango1.0-dev \
libstartup-notification0-dev \
libx11-xcb-dev \
libxcb-cursor-dev \
libxcb-icccm4-dev \
libxcb-keysyms1-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-util0-dev \
libxcb-xfixes0-dev \
libxcb-xinerama0-dev \
libxcb-xkb-dev \
libxcb-xrm-dev \
libxcb-xtest0-dev \
libxdg-basedir-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
xutils-dev \
lua-discount \
liblua5.3-dev \
lua5.3
- name: Install downloaded packages
run: |
sudo dpkg -i /var/cache/apt/archives/*.deb
- name: Cache luarocks
id: cache-luarocks
uses: actions/cache@v2
with:
path: /tmp/luarocks
key: ${{ github.workflow }}-${{ runner.os }}-luarocks-3.5.0
- name: Install fresh Luarocks
if: steps.cache-luarocks.outputs.cache-hit != 'true'
run: |
wget -O /tmp/luarocks.tar.gz https://github.com/luarocks/luarocks/archive/v3.5.0.tar.gz
mkdir /tmp/luarocks
tar -xf /tmp/luarocks.tar.gz -C /tmp/luarocks --strip-components=1
cd /tmp/luarocks
./configure
make build
sudo make install
- name: Install cached Luarocks
if: steps.cache-luarocks.outputs.cache-hit == 'true'
run: |
cd /tmp/luarocks
sudo make install
- name: Install rocks
run: |
sudo -H luarocks install lgi
sudo -H luarocks install ldoc
- uses: actions/checkout@v2
- name: Build Awesome version string
run: |
# If this workflow is triggered by a pull request, we get a base branch.
# Otherwise, check if the current commit has a meaningful name.
if [ -n "${{ github.base_ref }}" ]; then
AWESOME_VERSION="${{ github.base_ref }}"
else
AWESOME_VERSION="$(git rev-parse --abbrev-ref HEAD)"
fi
AWESOME_VERSION="${AWESOME_VERSION}-g$(git rev-parse --short HEAD)"
if [ "${{ github.event_name }}" == "pull_request" && -n "${{ matrix.test_prev_commits }}" ]; then
AWESOME_VERSION="${AWESOME_VERSION}-PR${{ github.event.number }}"
elif [ "${{ github.event_name }}" == "pull_request" ]; then
AWESOME_VERSION="v9999-PR${{ github.event.number }}"
fi
echo "AWESOME_VERSION=${AWESOME_VERSION}" >> ${GITHUB_ENV}
- name: Create Build Environment
run: cmake -E make_directory -B "${{ github.workspace }}/build"
- name: Configure CMake
run: |
cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \
-DAWESOME_VERSION=$AWESOME_VERSION \
-DGENERATE_DOC=ON \
-DGENERATE_MANPAGES=ON \
-DDO_COVERAGE=OFF
- name: Build
run: cd "${{ github.workspace }}/build" && make
- name: Run apidoc script
env:
PR_NUMBER: ${{ github.event.number }}
APIDOC_TOKEN: ${{ secrets.APIDOC_TOKEN || github.token }}
run: .github/scripts/apidoc.sh
# vim: filetype=yaml:expandtab:shiftwidth=2:tabstop=2

View File

@ -1,394 +0,0 @@
name: Build & Test
on:
# Trigger on push to branches `master` and `3.5`.
push:
branches: [ master, 3.5 ]
# Trigger on pull request events for PRs that have `master` as their target branch
pull_request:
branches: [ master ]
# Allow running the workflow manually
workflow_dispatch:
defaults:
run:
# GitHub Actions adds `errexit` and `pipefail` by default, but we add `xtrace`
# to improve debugging some of the longer scripts.
shell: /bin/bash -o errexit -o pipefail -o xtrace {0}
jobs:
main:
# The type of runner that the job will run on
runs-on: ubuntu-20.04
name: ${{ matrix.test_name }}
strategy:
# Let other jobs continue even if one of them fails
fail-fast: false
matrix:
include:
- test_name: "codecov-lua5.3"
lua_version: "5.3"
lua_name: "lua5.3"
coverage: "codecov"
manual_screens: true
- test_name: "coveralls-lua5.2"
lua_version: "5.2"
lua_name: "lua5.2"
coverage: "coveralls"
manual_screens: true
- test_name: "lua5.1"
lua_version: "5.1"
lua_name: "lua5.1"
- test_name: "luajit"
lua_version: "5.1"
lua_name: "luajit"
lua_library: "/usr/lib/x86_64-linux-gnu/libluajit-5.1.so"
lua_include: "/usr/include/luajit-2.1"
luarocks_args: "--lua-suffix=jit-2.1.0-beta3"
# Lua 5.2 with fixed lgi version and screen size not divisible by 2.
- test_name: "fixed-lgi-lua5.2"
lua_version: "5.2"
lua_name: "lua5.2"
lgi_version: "0.9.2"
tests_screen_size: "1921x1079"
check_qa: true
empty_theme_while_loading: true
test_prev_commits: true
env:
LUA: ${{ matrix.lua_version }}
LUAINCLUDE: ${{ matrix.lua_include || format('/usr/include/lua{0}', matrix.lua_version) }}
LUALIBRARY: ${{ matrix.lua_library || format('/usr/lib/x86_64-linux-gnu/liblua{0}.so', matrix.lua_version) }}
TESTS_SCREEN_SIZE: ${{ matrix.tests_screen_size }}
TEST_TIMEOUT: '100'
CI: 'true'
steps:
# Create a cache invalidation key based on the current year + week.
# This way, packages will be checked for updates once every week.
- name: Get Date
id: get-date
run: echo "::set-output name=date::$(/bin/date -u "+%Y%W")"
- name: Cache apt packages
id: cache-apt
uses: actions/cache@v2
with:
path: /var/cache/apt/archives
# The trailing number serves as a version flag that can be incremented
# to invalidate the cache after changing the list of packages.
key: ${{ github.workflow }}-${{ runner.os }}-${{ steps.get-date.outputs.date }}-apt-4
- name: Download apt packages
if: steps.cache-apt.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install --download-only -y --no-install-recommends \
asciidoctor \
cmake \
dbus-x11 \
gettext \
gir1.2-gtk-3.0 \
gir1.2-pango-1.0 \
git \
libdbus-1-dev \
libgirepository1.0-dev \
libnotify-bin \
libpango1.0-dev \
libstartup-notification0-dev \
libx11-xcb-dev \
libxcb-cursor-dev \
libxcb-icccm4-dev \
libxcb-keysyms1-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-util0-dev \
libxcb-xfixes0-dev \
libxcb-xinerama0-dev \
libxcb-xkb-dev \
libxcb-xrm-dev \
libxcb-xtest0-dev \
libxdg-basedir-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
wmctrl \
x11-apps \
xcb-proto \
xdotool \
xorg \
xserver-xephyr \
xterm \
xutils-dev \
xvfb \
zsh \
lua-discount
- name: Install downloaded packages
run: sudo dpkg -i /var/cache/apt/archives/*.deb
- name: Install Lua packages
run: |
if [ "${{ matrix.lua_name }}" = "luajit" ]; then
sudo apt-get install libluajit-5.1-dev luajit
fi
sudo apt-get install liblua${{ matrix.lua_version }}-dev lua${{ matrix.lua_version }}
- name: Cache luarocks
id: cache-luarocks
uses: actions/cache@v2
with:
path: /tmp/luarocks
# The build input for luarocks changes per test, so we need separate caches
key: ${{ github.workflow }}-${{ runner.os }}-${{ matrix.test_name }}-luarocks-3.5.0
- name: Install fresh Luarocks
if: steps.cache-luarocks.outputs.cache-hit != 'true'
run: |
wget -O /tmp/luarocks.tar.gz https://github.com/luarocks/luarocks/archive/v3.5.0.tar.gz
mkdir /tmp/luarocks
tar -xf /tmp/luarocks.tar.gz -C /tmp/luarocks --strip-components=1
cd /tmp/luarocks
./configure --lua-version=${{ matrix.lua_version }} --with-lua-include=${LUAINCLUDE} ${{ matrix.luarocks_args }}
make build
sudo make install
- name: Install cached Luarocks
if: steps.cache-luarocks.outputs.cache-hit == 'true'
run: |
cd /tmp/luarocks
sudo make install
- name: Cache xcb-errors
id: cache-xcb-errors
uses: actions/cache@v2
with:
path: /tmp/xcb-errors
key: ${{ github.workflow }}-${{ runner.os }}-xcb-errors-1.0
- name: Install fresh xcb-errors
if: steps.cache-xcb-errors.outputs.cache-hit != 'true'
run: |
git clone --recursive --depth 1 --branch 1.0 https://gitlab.freedesktop.org/xorg/lib/libxcb-errors.git /tmp/xcb-errors
cd /tmp/xcb-errors
./autogen.sh --prefix=/usr
make
sudo make install
- name: Install cached xcb-errors
if: steps.cache-xcb-errors.outputs.cache-hit == 'true'
run: |
cd /tmp/xcb-errors
sudo make install
- name: Install rocks
run: |
sudo -H luarocks install lgi ${{ matrix.lgi_version }}
sudo -H luarocks install ldoc
sudo -H luarocks install busted
- name: Install QA check rocks
if: matrix.check_qa
run: |
sudo -H luarocks install luacheck
sudo -H luarocks install depgraph
- name: Install cluacov rock
if: matrix.coverage
run: sudo -H luarocks install cluacov
- name: Install coveralls rock
if: matrix.coverage == 'coveralls'
run: sudo -H luarocks install luacov-coveralls
- name: Install codecov.io uploader
if: matrix.coverage == 'codecov'
run: wget -O /tmp/codecov-bash https://codecov.io/bash
# Check out repository to ${{ github.workspace }}
# Automatically picks the current branch/PR
- uses: actions/checkout@v2
- name: Create Build Environment
run: cmake -E make_directory -B "${{ github.workspace }}/build"
- name: Build Awesome version string
run: |
# If this workflow is triggered by a pull request, we get a base branch.
# Otherwise, check if the current commit has a meaningful name.
if [ -n "${{ github.base_ref }}" ]; then
AWESOME_VERSION="${{ github.base_ref }}"
else
AWESOME_VERSION="$(git rev-parse --abbrev-ref HEAD)"
fi
AWESOME_VERSION="${AWESOME_VERSION}-g$(git rev-parse --short HEAD)"
if [ "${{ github.event_name }}" == "pull_request" && -n "${{ matrix.test_prev_commits }}" ]; then
AWESOME_VERSION="${AWESOME_VERSION}-PR${{ github.event.number }}"
elif [ "${{ github.event_name }}" == "pull_request" ]; then
AWESOME_VERSION="v9999-PR${{ github.event.number }}"
fi
echo "AWESOME_VERSION=${AWESOME_VERSION}" >> ${GITHUB_ENV}
- name: Configure CMake
run: |
cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \
-DAWESOME_VERSION=$AWESOME_VERSION \
-DLUA_LIBRARY=$LUALIBRARY \
-DLUA_INCLUDE_DIR=$LUAINCLUDE \
-DGENERATE_DOC=OFF \
-DGENERATE_MANPAGES=OFF \
-DDO_COVERAGE=${{ matrix.coverage }} \
-DTEST_MANUAL_SCREENS=${{ matrix.manual_screens }} \
-DSTRICT_TESTS=true \
-DCMAKE_C_FLAGS="-Werror"
# Break beautiful so that trying to access the theme before `beautiful.init()` causes an error
- name: Patch beautiful for an empty default theme
if: matrix.empty_theme_while_loading
run: |
sed -i -e 's/theme = {}/theme = setmetatable({}, { __index = function() error("May not access theme before beautiful.init()") end })/' lib/beautiful/init.lua
grep -q 'May not access' lib/beautiful/init.lua
- name: Build
run: cd "${{ github.workspace }}/build" && make
# Executable needs to be run once to provide coverage results
- name: Install and run
run: |
cd "${{ github.workspace }}/build"
sudo make install
awesome --version
- name: Run integration tests
run: cd "${{ github.workspace }}/build" && make check-integration
- name: Run unit tests
run: cd "${{ github.workspace }}/build" && make check-unit
- name: Run examples tests
if: matrix.coverage
run: cd "${{ github.workspace }}/build" && make check-examples
- name: Run requires tests
if: matrix.coverage
run: cd "${{ github.workspace }}/build" && make check-requires
- name: Run themes tests
run: cd "${{ github.workspace }}/build" && make check-themes
- name: Upload Lua code coverage report
if: matrix.coverage == 'codecov'
run: |
luacov
bash /tmp/codecov-bash -f build/luacov.report.out -X gcov -X coveragepy -F luacov
- name: Upload C code coverage report
if: matrix.coverage == 'codecov'
run: |
# Report coverage for each .gcno file separately.
# gcov will create .gcov files for the same source (e.g. for
# globalconf.h from awesome.c.gcno and event.c.gcno).
i=0
cd "${{ github.workspace }}/build"
find -path "*/lgi-check.dir" -prune -o \( -name '*.gcno' -print \) | while read -r gcno; do
gcov -pb "$gcno"
mkdir gcov.$(( ++i ))
mv *.gcov "gcov.$i"
# Delete any files for /usr.
# They are not relevant and might cause "Invalid path part" errors
# with Code Climate.
find "gcov.$i" -maxdepth 1 -type f -name '#usr#*.gcov' -delete
done
# Upload to Codecov.
bash /tmp/codecov-bash -X gcov -X coveragepy -F gcov
- name: Merge coverage
if: matrix.coverage == 'coveralls'
# Coveralls doesn't support GitHub Actions with `service_name` + `service_job_id` (yet?),
# but `luacov-coveralls` behaves as if they did by default.
# We need to explicitly remove the `service_name`, so that Coveralls uses the
# `repo_token` instead.
# See "Referencing a repository": https://docs.coveralls.io/api-introduction
run: |
luacov-coveralls \
--verbose \
--merge \
--repo-token ${{ secrets.COVERALLS_REPO_TOKEN || github.token }} \
--service-name ""
# `check-qa` is the only test that doesn't get a coverage report, so it has to run after all of that.
- name: Run qa tests
if: matrix.check_qa
run: cd "${{ github.workspace }}/build" && make check-qa
# Check each commit separately (to make git-bisect less annoying).
- name: Test previous commits
if: matrix.test_prev_commits && github.event_name == 'pull_request'
run: |
git remote add fork "${{ github.event.pull_request.head.repo.git_url }}"
# `actions/checkout` creates a shallow repo (`--depth 1`) by default,
# which is fine for everything up until now. But we need individual commits now.
# And we only want to unshallow now, to not slow down the checkout for other jobs.
git fetch --unshallow --all
rev_list="$(git rev-list --bisect-all origin/${{ github.base_ref }}..fork/${{ github.head_ref }})"
# The most recent commit has already been tested. So if that's the
# only commit in the PR, we can stop here.
if [[ $(echo "$rev_list" | wc -l) -lt 2 ]]; then
exit 0
fi
commits="$(echo "$rev_list" | grep -v 'dist=0' | cut -d' ' -f 1)"
n="$(echo "$commits" | wc -l)"
echo "Testing $n commits:"
echo "$commits" | xargs -I{} git log -1 --pretty='%h %s' {}
failed=""
for commit in $commits; do
echo "Testing commit $commit"
# Some files are updated when compiling...
git checkout --force "$commit"
git show --stat --oneline
cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \
-DAWESOME_VERSION=$AWESOME_VERSION \
-DLUA_LIBRARY=$LUALIBRARY \
-DLUA_INCLUDE_DIR=$LUAINCLUDE \
-DGENERATE_DOC=OFF \
-DGENERATE_MANPAGES=OFF \
-DDO_COVERAGE=OFF \
-DTEST_MANUAL_SCREENS=${{ matrix.manual_screens }} \
-DSTRICT_TESTS=true \
-DCMAKE_C_FLAGS="-Werror"
cd "${{ github.workspace}}/build"
if ! ( make && make check-unit ); then
failed="$failed $commit"
fi
done
git checkout --quiet --force fork/${{ github.head_ref }}
if [ -n "$failed" ]; then
echo "Checks failed for these commits:"
for c in $failed; do
git show --no-patch --pretty="%h %s (%an, %ad)" "$c"
done
exit 1
fi
# vim: filetype=yaml:expandtab:shiftwidth=2:tabstop=2

View File

@ -49,7 +49,7 @@ globals = {
-- Enable cache (uses .luacheckcache relative to this rc file).
cache = true
-- Do not enable colors to make the CI output more readable.
-- Do not enable colors to make the Travis CI output more readable.
color = false
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -3,8 +3,7 @@ pull_request_rules:
conditions:
- label!=no-mergify
- '#approved-reviews-by>=2'
- status-success=Build & Test
- status-success=Update API docs
- status-success=Travis CI - Pull Request
- status-success=codecov/patch
- status-success=coverage/coveralls
actions:

340
.travis.yml Normal file
View File

@ -0,0 +1,340 @@
dist: xenial
language: c
# Build only master and stable branches. Other branches go through PRs.
branches:
only:
- master
- 3.5
addons:
apt:
packages: &BASE_PACKAGES
# Build dependencies.
# See also `apt-cache showsrc awesome | grep -E '^(Version|Build-Depends)'`.
- libnotify-bin
- libcairo2-dev
- gir1.2-gtk-3.0
- libpango1.0-dev
- libxcb-xtest0-dev
- libxcb-icccm4-dev
- libxcb-randr0-dev
- libxcb-keysyms1-dev
- libxcb-xinerama0-dev
- libdbus-1-dev
- libxdg-basedir-dev
- libstartup-notification0-dev
- imagemagick
- libxcb1-dev
- libxcb-shape0-dev
- libxcb-util0-dev
- libx11-xcb-dev
- libxcb-cursor-dev
- libxcb-xkb-dev
- libxcb-xfixes0-dev
- libxkbcommon-dev
- libxkbcommon-x11-dev
# Deps for tests.
- dbus-x11
- xterm
- xdotool
- wmctrl
- xterm
- xvfb
- zsh
- x11-apps
# Need xorg-macros
- xutils-dev
# lgi.
- gir1.2-pango-1.0
- libgirepository1.0-dev
jobs:
include:
- env: LUA=5.2 LUANAME=lua5.2 DO_COVERAGE=coveralls MANUAL_SCREENS=1
addons:
apt:
packages:
- *BASE_PACKAGES
- liblua5.2-dev
- lua5.2
- env: LUA=5.3 LUANAME=lua5.3 DO_COVERAGE=codecov MANUAL_SCREENS=1
addons:
apt:
packages:
- *BASE_PACKAGES
- liblua5.3-dev
- lua5.3
- env: LUA=5.1 LUANAME=lua5.1 BUILD_IN_DIR=/tmp/awesome-build WITH_XCB_ERRORS=yes
addons:
apt:
packages:
- *BASE_PACKAGES
- liblua5.1-dev
- lua5.1
# For xcb-errors
- xcb-proto
- env: LUA=5.1 LUANAME=luajit-2.0 LUALIBRARY=/usr/lib/x86_64-linux-gnu/libluajit-5.1.so LUAROCKS_ARGS=--lua-suffix=jit-2.0.4
addons:
apt:
packages:
- *BASE_PACKAGES
- libluajit-5.1-dev
- luajit
# Lua 5.2 with older lgi and screen size not divisible by 2.
# SOURCE_DATE_EPOCH: used for stable dates during build.
- env: LUA=5.2 LUANAME=lua5.2 LGIVER=0.8.0 TESTS_SCREEN_SIZE=1921x1079 BUILD_APIDOC=true DO_CHECKQA=1 EMPTY_THEME_WHILE_LOADING=1 SOURCE_DATE_EPOCH=1893456000 TEST_PREV_COMMITS=1
addons:
apt:
packages:
- *BASE_PACKAGES
- liblua5.2-dev
- lua5.2
- lua-discount
env:
global:
# Secure GH_APIDOC_TOKEN to push to awesomeWM/apidoc.
- secure: "R/HYDclnws1I1+v9Yjt+RKa4CsFhbBT9tiwE3EfPhEj2KCYX4sFRMxuZvLf5sq0XWdrQaPhQ54fgAZGr3f054JKRXcTB0g9J6nhSHz9kIjPh446gafUhEeDQcZRwM/MeCWiwFIkiZm6smYoDFE9JTWu6quNV+lQ4kcVDOp2ibEc="
before_install:
- if [ "$BUILD_APIDOC" = true ] && [ -n "$DO_COVERAGE" ]; then echo "BUILD_APIDOC and DO_COVERAGE are not meant to be used together." >&2; exit 1; fi
- if [ -z $LUAINCLUDE ]; then LUAINCLUDE=/usr/include/${LUANAME}; fi
- if [ -z $LUALIBRARY ]; then LUALIBRARY=/usr/lib/x86_64-linux-gnu/lib${LUANAME}.so; fi
- cmake --version
install:
- sudo gem install asciidoctor
- |
# Install xcb-util-xrm.
set -e
(git clone --depth 1 --recursive https://github.com/Airblader/xcb-util-xrm.git /tmp/xcb-util-xrm
cd /tmp/xcb-util-xrm
./autogen.sh --prefix=/usr
make && sudo make install)
# Install xcb-errors if needed
- |
set -e
if [[ "$WITH_XCB_ERRORS" == "yes" ]]; then
git clone --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-errors.git /tmp/xcb-errors
(cd /tmp/xcb-errors
./autogen.sh --prefix=/usr
make && sudo make install)
fi
- |
# Install Lua (per env).
if [[ "$LUANAME" == "luajit-2.0" ]]; then
# "Create" /usr/bin/lua if needed (Yup, this is a bad hack)
if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/luajit /usr/bin/lua; fi
else
# lua5.3 does not install /usr/bin/lua, but lua5.1/lua5.2 do.
if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/${LUANAME} /usr/bin/lua; fi
fi
- lua -v
# Install luarocks (for the selected Lua version).
- |
travis_retry wget https://github.com/luarocks/luarocks/archive/v3.0.4.tar.gz
tar xf v3.0.4.tar.gz -C /tmp
(cd /tmp/luarocks-* \
&& ./configure --lua-version=$LUA --with-lua-include=${LUAINCLUDE} ${LUAROCKS_ARGS} \
&& make build \
&& sudo make install)
- travis_retry sudo luarocks install lgi $LGIVER
# Install busted for "make check-unit".
- travis_retry sudo luarocks install busted
# Install luacheck for "make check-qa".
- if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install luacheck; fi
# Install depgraph for "make check-qa".
- if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install depgraph; fi
# Install ldoc for building docs and check-ldoc-warnings.
- |
if [ "$BUILD_APIDOC" = "true" ] || [ "$DO_CHECKQA" = 1 ]; then
travis_retry sudo luarocks install ldoc
fi
# Install dependencies for code coverage testing.
- if [ -n "$DO_COVERAGE" ]; then sudo luarocks install cluacov; fi
- if [ "$DO_COVERAGE" = "coveralls" ]; then sudo luarocks install luacov-coveralls; fi
# Determine custom version.
- export AWESOME_VERSION="${TRAVIS_BRANCH}-g$(git rev-parse --short HEAD)"
- |
if [ "$TRAVIS_PULL_REQUEST" != false ] && [ "$TEST_PREV_COMMITS" = 1 ]; then
AWESOME_VERSION="${AWESOME_VERSION}-PR${TRAVIS_PULL_REQUEST}"
elif [ "$TRAVIS_PULL_REQUEST" != false ]; then
AWESOME_VERSION="v9999-PR${TRAVIS_PULL_REQUEST}";
fi
# function for codecov support
- if [ "$DO_COVERAGE" = "codecov" ]; then travis_retry wget -O /tmp/codecov-bash https://codecov.io/bash; fi
- |
do_codecov() {
echo "== do_codecov in $PWD: $*: build/luacov.stats.out: =="
if [ "$DO_COVERAGE" = "codecov" ]; then
test -f build/luacov.stats.out || { echo 'build/luacov.stats.out does not exist.'; return 1; }
luacov || return 1
travis_retry bash /tmp/codecov-bash -f build/luacov.report.out -X gcov -X coveragepy -F luacov -e LUANAME,LGIVER || return 1
rm build/luacov.report.out build/luacov.stats.out
fi
return 0
}
- |
do_codecov_gcov() {
echo "== do_codecov_gcov in $PWD: $*: =="
if [ "$DO_COVERAGE" = "codecov" ]; then
# Report coverage for each .gcno file separately.
# gcov will create .gcov files for the same source (e.g. for
# globalconf.h from awesome.c.gcno and event.c.gcno).
i=0
set -x
(
cd build
find -path "*/lgi-check.dir" -prune -o \( -name '*.gcno' -print \) | while read -r gcno; do
gcov -pb "$gcno"
mkdir gcov.$(( ++i ))
mv *.gcov "gcov.$i"
# Delete any files for /usr.
# They are not relevant and might cause "Invalid path part" errors
# with Code Climate.
find "gcov.$i" -maxdepth 1 -type f -name '#usr#*.gcov' -delete
done
# Upload to Codecov.
travis_retry bash /tmp/codecov-bash -X gcov -X coveragepy -F gcov -e LUANAME,LGIVER
)
fi
return 0
}
# Functions for custom Travis folds.
- |
travis_fold_start() {
travis_fold start "$1"
travis_fold_current="$1"
}
travis_fold_end() {
travis_fold end "$travis_fold_current"
}
travis_run_in_fold() {
travis_fold_start "$1"
shift
"$@" || return 1
travis_fold_end
return 0
}
script:
- if [ "$MANUAL_SCREENS" != "1" ]; then export MANUAL_SCREENS=0; fi
- export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -D LUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION -D STRICT_TESTS=true -D DO_COVERAGE=$DO_COVERAGE -D TEST_MANUAL_SCREENS=$MANUAL_SCREENS -D CMAKE_C_FLAGS=-Werror"
- |
if [ "$EMPTY_THEME_WHILE_LOADING" = 1 ]; then
# Break beautiful so that trying to access the theme before beautiful.init() causes an error
sed -i -e 's/theme = {}/theme = setmetatable({}, { __index = function() error("May not access theme before beautiful.init()") end })/' lib/beautiful/init.lua \
&& grep -q 'May not access' lib/beautiful/init.lua
fi
- |
set -e
if [ -n "$BUILD_IN_DIR" ]; then
# Explicitly remove the Makefile to not build from the src dir accidentally.
rm Makefile
SOURCE_DIRECTORY="$PWD"
mkdir "$BUILD_IN_DIR"
cd "$BUILD_IN_DIR"
travis_run_in_fold "build_in_dir" cmake $CMAKE_ARGS "$SOURCE_DIRECTORY"
fi
- travis_run_in_fold "make" make ; sleep 5
- make ; sleep 5
- make ; sleep 5
- make ; sleep 5
- |
if [ "$TRAVIS_TEST_RESULT" = 0 ]; then
travis_run_in_fold "make.install" sudo env PATH=$PATH make install
awesome --version
fi
# Run checks.
- |
if [ "$TRAVIS_TEST_RESULT" = 0 ]; then
set -ex
if [ -n "$DO_COVERAGE" ]; then
travis_fold_start "DO_COVERAGE"
# Run tests/examples explicitly.
make check-examples || exit 1
do_codecov samples
make check-unit || exit 1
do_codecov unittests
make check-integration || exit 1
do_codecov functionaltests
make check-themes || exit 1
do_codecov themes
do_codecov_gcov c_code
travis_fold_end
else
# TODO: does not run check-examples. Should it?
travis_run_in_fold "make.check-unit" make check-unit
travis_run_in_fold "make.check-integration" make check-integration
travis_run_in_fold "make.check-themes" make check-themes
fi
set +x
fi
# Run check-qa.
- |
if [ "$DO_CHECKQA" = 1 ]; then
travis_run_in_fold "make.check-qa" make check-qa
fi
- |
if [ "$TEST_PREV_COMMITS" = 1 ] && ! [ "$TRAVIS_PULL_REQUEST" = false ]; then
set -e
# Check each commit separately (to make git-bisect less annoying).
# Fix Travis' commit range (https://github.com/travis-ci/travis-ci/issues/4596).
commit_range="${TRAVIS_COMMIT_RANGE/.../..}"
echo "Testing previous commits ($commit_range)"
rev_list="$(git rev-list --bisect-all $commit_range)"
echo "rev-list: $rev_list"
commits="$(echo "$rev_list" | grep -v 'dist=0' | cut -d\ -f 1)"
n="$(echo "$commits" | wc -l)"
echo "Testing $n commits: $commits"
i=0
failed=
for commit in $commits; do
i=$((i+1))
travis_fold_start "test_commit_${commit}_.$i.$n"
echo "Testing commit $commit"
git reset --hard # Some files are updated when compiling...
git checkout "$commit"
git --no-pager show --stat
if ! make all check CMAKE_ARGS+="-D DO_COVERAGE=0"; then
failed="$failed $commit"
fi
travis_fold_end
done
git checkout -qf FETCH_HEAD
if [ -n "$failed" ]; then
echo "Checks failed for these commits:"
for c in $failed; do
git log -1 --pretty="%h %s (%an, %ad)" "$c"
done
false
fi
fi
after_success:
# Push updated API docs for relevant branches, e.g. non-PRs builds on master.
- if [ "$BUILD_APIDOC" = "true" ]; then build-utils/travis-apidoc.sh; fi
# Push code coverage information
- |
set -e
if [ "$DO_COVERAGE" = "coveralls" ]; then
test -f build/luacov.stats.out || { echo 'build/luacov.stats.out does not exist.'; return 1; }
luacov-coveralls --verbose --merge
fi

View File

@ -619,14 +619,13 @@ main(int argc, char **argv)
bool success = true;
/* Get the first config that will be tried */
const char *config = luaA_find_config(&xdg, confpath, true_config_callback);
fprintf(stdout, "Checking config '%s'... ", config);
/* Try to parse it */
lua_State *L = luaL_newstate();
if(luaL_loadfile(L, config))
{
const char *err = lua_tostring(L, -1);
fprintf(stdout, "\nERROR: %s\n", err);
fprintf(stderr, "%s\n", err);
success = false;
}
p_delete(&config);
@ -634,11 +633,12 @@ main(int argc, char **argv)
if(!success)
{
fprintf(stderr, "✘ Configuration file syntax error.\n");
return EXIT_FAILURE;
}
else
{
fprintf(stdout, "OK\n");
fprintf(stderr, "✔ Configuration file syntax OK.\n");
return EXIT_SUCCESS;
}
}

View File

@ -71,14 +71,6 @@ if (NOT LUA_FOUND)
"You might want to hint it using the LUA_DIR environment variable, "
"or set the LUA_INCLUDE_DIR / LUA_LIBRARY CMake variables.")
endif()
set(LUA_FULL_VERSION "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
# 5.1 <= LUA_VERSION < 5.4
if(NOT ((LUA_FULL_VERSION VERSION_EQUAL 5.1.0 OR LUA_FULL_VERSION VERSION_GREATER 5.1.0) AND LUA_FULL_VERSION VERSION_LESS 5.4.0))
message(FATAL_ERROR "Awesome only supports Lua versions 5.1-5.3, please refer to"
"https://awesomewm.org/apidoc/documentation/10-building-and-testing.md.html#Building")
endif()
# }}}
# {{{ Check if documentation can be build

View File

@ -77,7 +77,7 @@ mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
-- }}}
-- {{{ Tag layout
-- {{{ Tag
-- @DOC_LAYOUT@
-- Table of layouts to cover with awful.layout.inc, order matters.
tag.connect_signal("request::default_layouts", function()
@ -99,27 +99,6 @@ tag.connect_signal("request::default_layouts", function()
end)
-- }}}
-- {{{ Wallpaper
-- @DOC_WALLPAPER@
screen.connect_signal("request::wallpaper", function(s)
awful.wallpaper {
screen = s,
widget = {
{
image = beautiful.wallpaper,
upscale = true,
downscale = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
end)
-- }}}
-- {{{ Wibar
-- Keyboard map indicator and switcher
@ -128,6 +107,19 @@ mykeyboardlayout = awful.widget.keyboardlayout()
-- Create a textclock widget
mytextclock = wibox.widget.textclock()
-- @DOC_WALLPAPER@
screen.connect_signal("request::wallpaper", function(s)
-- Wallpaper
if beautiful.wallpaper then
local wallpaper = beautiful.wallpaper
-- If wallpaper is a function, call it with the screen
if type(wallpaper) == "function" then
wallpaper = wallpaper(s)
end
gears.wallpaper.maximized(wallpaper, s, true)
end
end)
-- @DOC_FOR_EACH_SCREEN@
screen.connect_signal("request::desktop_decoration", function(s)
-- Each screen has its own tag table.
@ -187,11 +179,11 @@ screen.connect_signal("request::desktop_decoration", function(s)
-- @DOC_WIBAR@
-- Create the wibox
s.mywibox = awful.wibar {
position = "top",
screen = s,
s.mywibox = awful.wibar({ position = "top", screen = s })
-- @DOC_SETUP_WIDGETS@
widget = {
-- Add widgets to the wibox
s.mywibox.widget = {
layout = wibox.layout.align.horizontal,
{ -- Left widgets
layout = wibox.layout.fixed.horizontal,
@ -208,7 +200,6 @@ screen.connect_signal("request::desktop_decoration", function(s)
s.mylayoutbox,
},
}
}
end)
-- }}}

View File

@ -63,6 +63,7 @@ local allowed_deps = {
},
-- TODO: Get rid of these
["gears.surface"] = { ["wibox.hierarchy"] = true },
["gears.connection"] = { ["*"] = true },
}
-- Turn "foo.bar.baz" into "foo.bar". Returns nil if there is nothing more to

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
#
# Process (API) docs after a successful build on GitHub Actions.
# Process (API) docs after a successful build on Travis (via ../.travis.yml).
#
# Updated/changed documentation for "master" is pushed to gh-pages.
# In case of pull requests or other branches, it will get added to a separate branch.
@ -9,49 +9,48 @@
# NOTE: stdout/stderr might/should be discarded to not leak sensitive information.
echo "Post-processing (API) documentation."
echo "PR Number: $PR_NUMBER"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "TRAVIS_PULL_REQUEST: $TRAVIS_PULL_REQUEST"
echo "TRAVIS_BRANCH: $TRAVIS_BRANCH"
if [ -z "$APIDOC_TOKEN" ]; then
echo "No APIDOC_TOKEN available. Skipping."
if [ -z "$GH_APIDOC_TOKEN" ]; then
echo "No GH_APIDOC_TOKEN available. Skipping."
exit
fi
# NOTE: DO NOT USE "set -x", or anything else that would reveal APIDOC_TOKEN!
# GitHub Actions does filter secrets, but extra caution won't hurt
# NOTE: DO NOT USE "set -x", or anything else that would reveal GH_APIDOC_TOKEN!
set -e
set +x
# Display exit code in case of failure (probably due to 'set -x').
# Display exit code in term of failure (probably due to 'set -x').
trap '[ "$?" = 0 ] || echo "EXIT CODE: $?"' EXIT
REPO_APIDOC="https://${APIDOC_TOKEN}@github.com/awesomeWM/apidoc"
REPO_APIDOC="https://${GH_APIDOC_TOKEN}@github.com/awesomeWM/apidoc"
REPO_DIR="$PWD"
export GIT_AUTHOR_NAME="awesome-robot on GitHub Actions"
export GIT_AUTHOR_NAME="awesome-robot on Travis CI"
export GIT_AUTHOR_EMAIL="awesome-robot@users.noreply.github.com"
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
git clone --depth 1 --branch gh-pages "$REPO_APIDOC" build/apidoc \
2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g"
2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g"
cd build/apidoc
# This will re-use already existing branches (updated PR).
if [ "$PR_NUMBER" != false ]; then
BRANCH="pr-$PR_NUMBER"
elif [ "$GITHUB_HEAD_REF" != master ]; then
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
BRANCH="pr-$TRAVIS_PULL_REQUEST"
elif [ "$TRAVIS_BRANCH" != master ]; then
# Use merge-base of master in branch name, to keep different branches with
# the same name apart.
# shellcheck disable=SC2015
BRANCH="$GITHUB_HEAD_REF-$(cd "$REPO_DIR" \
BRANCH="$TRAVIS_BRANCH-$(cd "$REPO_DIR" \
&& git fetch --unshallow origin master \
&& git rev-parse --short "$(git merge-base HEAD FETCH_HEAD || true)" || true)"
else
BRANCH="gh-pages"
fi
if [ "$BRANCH" != "gh-pages" ]; then
git checkout -b "$BRANCH" "origin/${BRANCH}" 2> /dev/null || git checkout -b "$BRANCH"
git checkout -b "$BRANCH" "origin/${BRANCH}" || git checkout -b "$BRANCH"
fi
# Use a temporary branch for the two commits, which allows for a better UI.
@ -63,7 +62,7 @@ cat > ../doc/README.md <<END
This repository contains the built API documentation for the
[awesome](https://github.com/awesomeWM/awesome) window manager. It is
automatically updated via GitHub Actions when the master branch changes. Hence:
automatically updated via Travis when the master branch changes. Hence:
## Do NOT send pull requests here
@ -86,21 +85,21 @@ LAST_COMMIT_MSG="$(cd "$REPO_DIR" && git log -1 --pretty=format:%s)"
LAST_COMMIT="$(cd "$REPO_DIR" && git rev-parse --short HEAD)"
# Commit the relevant changes.
COMMIT_MSG="Update docs for $AWESOME_VERSION via Github Actions
COMMIT_MSG="Update docs for $AWESOME_VERSION via Travis
Last commit message:
$LAST_COMMIT_MSG
Commits: https://github.com/awesomeWM/awesome/compare/${GITHUB_BASE_REF}..${GITHUB_HEAD_REF}
Build URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
Commits: https://github.com/awesomeWM/awesome/compare/${TRAVIS_COMMIT_RANGE/.../..}
Build URL: https://travis-ci.com/awesomeWM/awesome/builds/${TRAVIS_BUILD_ID}"
git commit -m "[relevant] $COMMIT_MSG"
# Commit the irrelevant changes.
mv .git ../doc
cd ../doc
git add --all .
if git commit -m "[boilerplate] $COMMIT_MSG"; then
git commit -m "[boilerplate] $COMMIT_MSG"
# Reorder/swap commits, to have "relevant" after "boilerplate".
# This makes it show up earlier in the Github interface etc.
git tag _old
@ -108,13 +107,12 @@ if git commit -m "[boilerplate] $COMMIT_MSG"; then
git cherry-pick _old _old~1
RELEVANT_REV="$(git rev-parse --short HEAD)"
git tag -d _old
fi
git checkout "$BRANCH"
OLD_REV="$(git rev-parse --short HEAD)"
if [ "$PR_NUMBER" != false ]; then
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
MERGE_COMMIT_MSG="$COMMIT_MSG
Pull request: https://github.com/awesomeWM/awesome/pull/${PR_NUMBER}"
Pull request: https://github.com/awesomeWM/awesome/pull/${TRAVIS_PULL_REQUEST}"
else
PR_OR_ISSUE="$(echo "$COMMIT_MSG" | head -n 1 | grep -o '#[0-9]\+' || true)"
if [ -n "$PR_OR_ISSUE" ]; then
@ -136,7 +134,7 @@ fi
git merge --no-ff -m "$MERGE_COMMIT_MSG" merged-update
NEW_REV="$(git rev-parse --short HEAD)"
git push origin "$BRANCH" 2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g"
git push origin "$BRANCH" 2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g"
# Generate compare view links.
# NOTE: use "\n" for line endings, not real ones for valid json!
@ -146,14 +144,14 @@ if [ "$BRANCH" != "gh-pages" ]; then
COMPARE_LINKS="$COMPARE_LINKS\nComparison against master (gh-pages): https://github.com/awesomeWM/apidoc/compare/gh-pages...${NEW_REV}"
fi
# shellcheck disable=SC2028
printf %s "Compare links:\n$COMPARE_LINKS"
echo "Compare links:\n$COMPARE_LINKS"
# Post a comment to the PR.
if [ "$PR_NUMBER" != false ]; then
curl -H "Authorization: token $APIDOC_TOKEN" \
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
curl -H "Authorization: token $GH_APIDOC_TOKEN" \
-d "{\"body\": \"Documentation has been updated for this PR.\n\n$COMPARE_LINKS\"}" \
"https://api.github.com/repos/awesomeWM/awesome/issues/${PR_NUMBER}/comments" \
2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g"
"https://api.github.com/repos/awesomeWM/awesome/issues/${TRAVIS_PULL_REQUEST}/comments" \
2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g"
fi
# vim: filetype=sh:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -124,10 +124,6 @@ to an object such as the mouse.
The `naughty.layout.box` allows to provide custom widgets to use within the
notifications.
The `awful.wallpaper` provides a non-intereactive "backgroud" for one or more
`screen`. While it uses normal widget, it will not automatically be repainted
if they change. It will also not provide any mouse events.
Finally, the `awful.titlebar`, while not technically a real `wibox`, acts
exactly the same way and allows to attach widgets on each side of clients.
@ -377,50 +373,6 @@ Code:
layout = wibox.layout.fixed.horizontal,
}
### Accessing widgets
For each widget or container, it is possible to add an `identifier` attribute
so that it can be accessed later.
Widgets defined using `setup` can be accessed using these methods:
* Avoiding the issue by using externally created widgets
* Using `my_wibox.my_first_widget.my_second_widget` style access
* Using JavaScript like `my_wibox:get_children_by_id("my_second_widget")[1]`
The first method mixes the imperative and declarative syntax, and makes the code
less readable. The second is a little verbose and only works if every node in
the chain has a valid identifier. The last one doesn't require long paths,
but it is not easy to get a specific instance if multiple widgets have the
same identifier.
WARNING: The widget identifier must not use a reserved name. This includes all
method names, existing widget attributes, `layout` and `widget`. Names should
also respect the Lua variable conventions (case-sensitive, alphanumeric,
underscore characters and non-numeric first character).
Code:
s.mywibox : setup {
{
id = "second",
widget = wibox.widget.textbox
},
{
id = "third",
widget = wibox.widget.textbox
},
id = "first",
layout = wibox.layout.fixed.horizontal,
}
s.mywibox.first.second:set_markup("changed!")
s.mywibox:get_children_by_id("third")[1]:set_markup("Also changed!")
### Extending the system
This system is very flexible. Each section attribute (the entries with string
@ -522,3 +474,99 @@ necessary for three reasons:
at a later time (by the parent widget).
* The code is highly redundant and some of the logic is delegated to the parent
widget to simplify everything.
## Accessing and updating widgets
There is 3 main ways to update the widgets. Each is best suited for it's own niche.
Choose the one that better suites the style of your code.
### Imperative
For each widget or container, it is possible to add an `identifier` attribute
so that it can be accessed later.
Widgets defined using `setup` can be accessed using these methods:
* Avoiding the issue by using externally created widgets
* Using `my_wibox.my_first_widget.my_second_widget` style access
* Using JavaScript like `my_wibox:get_children_by_id("my_second_widget")[1]`
The first method mixes the imperative and declarative syntax, and makes the code
less readable. The second is a little verbose and only works if every node in
the chain has a valid identifier. The last one doesn't require long paths,
but it is not easy to get a specific instance if multiple widgets have the
same identifier.
WARNING: The widget identifier must not use a reserved name. This includes all
method names, existing widget attributes, `layout` and `widget`. Names should
also respect the Lua variable conventions (case-sensitive, alphanumeric,
underscore characters and non-numeric first character).
Code:
s.mywibox : setup {
{
id = "second",
widget = wibox.widget.textbox
},
{
id = "third",
widget = wibox.widget.textbox
},
id = "first",
layout = wibox.layout.fixed.horizontal,
}
s.mywibox.first.second:set_markup("changed!")
s.mywibox:get_children_by_id("third")[1]:set_markup("Also changed!")
### Reactive
![Reactive update](../images/gears_reactive.svg)
Think of reactive programming like Excel/Spreadsheets. You define rules or even
business logic that will automatically be re-evaluated every time the data it
uses changes. In the background, this rewrites the function and automatically
creates all the signal connections. Lua is not a reactive language, so it has it's
limits and you should read the rules defined in the `gears.reactive` documentation
carefully. However, when it works, it is by far the simplest way to update a
widget defined using the declarative syntax.
#### Reactive expressions
A reactive expression is just a function wrapped by a `gears.reactive` object. This
will introspect the content and write all the boilerplate. Note that this *only* works
in declarative trees. It *cannot* be mixed with the imperative API.
@DOC_wibox_decl_doc_reactive_expr1_EXAMPLE@
Unlike QML, AwesomeWM also support extracting reactive blocks out of the tree:
@DOC_wibox_decl_doc_reactive_expr2_EXAMPLE@
#### Watcher objects
`gears.watcher` objects poll files or command at an interval. They do so using
background threads, so it wont affect performance much. They can be attached
directly to a widget or used with a `gears.connection` as demonstrated below.
Using `gears.connection` is preferred when the value is needed by multiple
widgets (see the CPU graph example later in this document).
@DOC_wibox_widget_progressbar_watcher_EXAMPLE@
### Declarative
The other way to interact with widgets is by creating `gears.connection` objects.
They can be added in your declarative widget tree like this:
@DOC_wibox_decl_doc_connection_EXAMPLE@
It is also possible to use them to bind non-widget objects with widgets:
@DOC_wibox_decl_doc_connection3_EXAMPLE@
One useful feature is that when the `gears.connection` has a callback, it has
direct access to all `id`s as variables or using `get_children_by_id`:
@DOC_wibox_decl_doc_connection5_EXAMPLE@

View File

@ -72,17 +72,6 @@ variables such as `bg_normal`. To get a list of all official variables, see
the [appearance guide](../documentation/06-appearance.md.html).
]]
sections.DOC_WALLPAPER = [[
The AwesomeWM wallpaper module, `awful.wallpaper` support both per-screen wallpaper
and wallpaper across multiple screens. In the default configuration, the `"request::wallpaper"` signal
is emitted everytime a screen is added, moved, resized or when the bars
(`awful.wibar`) are moved.
This is will suited for single-screen wallpapers. If you wish to use multi-screen wallpaper,
it is better to create a global wallpaper object and edit it when the screen change. See
the `add_screen`/`remove_screens` methods and the `screens` property of `awful.wallpaper` for
examples.
]]
sections.DOC_DEFAULT_APPLICATIONS = [[
&nbsp;

View File

@ -25,10 +25,10 @@ have not opened any programs. On the top right you see the time/date and a
symbol showing the current layout. You can also click on the symbol to change
the active layout.
One of the big advantages of Awesome over other tiling window managers is its
good mouse support. Awesome can act as a full floating window manager (almost
like openbox) if you want. For this basic tutorial we will mainly focus on
keyboard control, so let's learn some key bindings now.
One of the big advantages of Awesome over other tiling window managers is its good
mouse support. Awesome can act as a full floating window manager (almost like
openbox) if you want. For this basic tutorial we will mainly focus on keyboard
control, so let's learn some key bindings now.
Let's open a terminal: press *Mod4+Enter*. Mod4 is your "Windows key", the key
between Ctrl and Alt. You can change the modkey if you want, but we'll get to
@ -65,23 +65,20 @@ overview now also provides a cheat sheet for controlling Vim.
## Change the theme
Awesome has multiple builtin themes you can choose from:
Awesome has four themes you can choose from: *default*, *sky*, *xresources*, and
*zenburn*.
* *default*
* *gtk*
* *sky*
* *xresources*
* *zenburn*
To change the theme, open your `rc.lua`, find this line near the beginning of
the file, and change `default` to one of the other values mentioned:
To change the theme, open your rc.lua and edit this line near the beginning of
the file:
beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua")
However, for this tutorial we will copy and customize the default theme.
Copy `/usr/share/awesome/themes/default/theme.lua` to `~/.config/awesome/`
and change the line shown above in `rc.lua` like this. Make sure to replace
`USER` with your user name.
For this tutorial we will stick with the default theme.
Now we will customize the theme. Copy
`/usr/share/awesome/themes/default/theme.lua` to `~/.config/awesome/` and change
the above line in your theme like this (remember to replace `USER` with your
user name):
beautiful.init("/home/USER/.config/awesome/theme.lua")
@ -92,9 +89,7 @@ this line in your theme file:
theme.wallpaper = themes_path.."default/background.png"
The default uses a path relative to `themes_path` by using the `..` operator to
join two strings together. To just set it to an absolute path for example,
you could do:
The default uses a path relative to `themes_path` by using the `..` operator to join two strings together. To just set it to an absolute path for example, you could do:
theme.wallpaper = "/usr/share/backgrounds/my-awesome-wallpaper.png"

View File

@ -36,7 +36,8 @@ Do **not** use such functions and prefer `awful.spawn.easy_async`,
fast enough and won't impact the main event loop iteration time, you are wrong.
*Every* calls to `io.open` are impacted by the system `iowait` queue and can
spend hundreds of milliseconds blocked *before* being executed. Note that
some common widget or probe libraries do not follow this
some common widget or probe libraries such as
[Vicious](https://github.com/Mic92/vicious) do not follow this
advice currently and are known to cause input lag on some systems (but not all).
In both case, a warning like:
@ -52,19 +53,6 @@ provide a theme daemon. For more information about how to manage the
look and feel of applications, refer to the
[Arch Linux Wiki](https://wiki.archlinux.org/index.php/Category:Eye_candy).
### Awesome doesn't show up on my login screen
There have been cases where Awesome wasn't correctly registered with the display manager,
usually due to a missing `.desktop` file.
To fix such issues, copy `awesome.desktop` from the root of [the repository](https://github.com/awesomeWM/awesome/) to `/usr/share/xsessions/`.
```shell
curl https://raw.githubusercontent.com/awesomeWM/awesome/master/awesome.desktop | sudo tee /usr/share/xsessions/awesome.desktop
```
If you installed Awesome through a package manager, you might want to check if the package includes that file and, if not, notify the maintainer to add it as appropriate.
## Configuration
### How to change the default window management layout?

View File

@ -121,8 +121,6 @@ local function parse_files(paths, property_name, matcher, name_matcher)
local exp2 = matcher or "[-*]*[ ]*".. property_name ..".(.+)"
local exp3 = name_matcher or "[. ](.+)"
local count = 0
local names = {} -- Used to check for duplicates
local ret = {}
table.sort(paths)
@ -164,47 +162,13 @@ local function parse_files(paths, property_name, matcher, name_matcher)
"seems to be misformatted. Use `beautiful.namespace_name`"
)
else
local insert_name = name:gsub("_", "_")
local link = get_link(file, var, var:match(exp3):gsub("_", "\\_"))
local desc = buffer:gmatch("[-*/ \n]+([^\n.]*)")() or ""
local mod = path_to_module(file)
if names[insert_name] == nil then
count = count + 1
table.insert(ret, count, {
table.insert(ret, {
file = file,
name = insert_name,
link = link,
desc = desc,
mod = mod
name = name:gsub("_", "_"),
link = get_link(file, var, var:match(exp3):gsub("_", "\\_")),
desc = buffer:gmatch("[-*/ \n]+([^\n.]*)")() or "",
mod = path_to_module(file),
})
names[insert_name] = count
else
link = link .. "(" .. mod .. ")"
if type(ret[names[insert_name]].link) ~= "table" then
ret[names[insert_name]].file = {
ret[names[insert_name]].file,
file
}
ret[names[insert_name]].link = {
ret[names[insert_name]].link .. " (" .. ret[names[insert_name]].mod .. ")",
link
}
ret[names[insert_name]].desc = {
ret[names[insert_name]].desc,
desc
}
ret[names[insert_name]].mod = {
ret[names[insert_name]].mod,
mod
}
else
table.insert(ret[names[insert_name]].file, file)
table.insert(ret[names[insert_name]].link, link)
table.insert(ret[names[insert_name]].desc, desc)
table.insert(ret[names[insert_name]].mod, mod)
end
end
end
buffer = ""
@ -226,16 +190,8 @@ local function create_table(entries, columns, prefix)
local line = " <tr>"
for _, column in ipairs(columns) do
if type(entry[column]) == "table" then
line = line .. "<td><ul>"
for _,v in pairs(entry[column]) do
line = line .. "<li>" .. v .. "</li>"
end
line = line .. "</ul></td>"
else
line = line.."<td>"..entry[column].."</td>"
end
end
table.insert(lines, prefix..line.."</tr>\n")
end

View File

@ -1,80 +1,80 @@
<div class='flex-list'>
* <div>num_glyphs</div>
* <div>arrow</div>
* <div>based_arrow_down</div>
* <div>based_arrow_up</div>
* <div>boat</div>
* <div>bogosity</div>
* <div>bottom_left_corner</div>
* <div>bottom_right_corner</div>
* <div>bottom_side</div>
* <div>bottom_tee</div>
* <div>box_spiral</div>
* <div>center_ptr</div>
* <div>circle</div>
* <div>clock</div>
* <div>coffee_mug</div>
* <div>cross</div>
* <div>crosshair</div>
* <div>cross_reverse</div>
* <div>cursor</div>
* <div>diamond_cross</div>
* <div>dotbox</div>
* <div>dot</div>
* <div>double_arrow</div>
* <div>draft_large</div>
* <div>draft_small</div>
* <div>draped_box</div>
* <div>exchange</div>
* <div>fleur</div>
* <div>gobbler</div>
* <div>gumby</div>
* <div>hand</div>
* <div>hand</div>
* <div>heart</div>
* <div>icon</div>
* <div>iron_cross</div>
* <div>leftbutton</div>
* <div>left_ptr</div>
* <div>left_side</div>
* <div>left_tee</div>
* <div>ll_angle</div>
* <div>lr_angle</div>
* <div>man</div>
* <div>middlebutton</div>
* <div>mouse</div>
* <div>pencil</div>
* <div>pirate</div>
* <div>plus</div>
* <div>question_arrow</div>
* <div>rightbutton</div>
* <div>right_ptr</div>
* <div>right_side</div>
* <div>right_tee</div>
* <div>rtl_logo</div>
* <div>sailboat</div>
* <div>sb_down_arrow</div>
* <div>sb_h_double_arrow</div>
* <div>sb_left_arrow</div>
* <div>sb_right_arrow</div>
* <div>sb_up_arrow</div>
* <div>sb_v_double_arrow</div>
* <div>shuttle</div>
* <div>sizing</div>
* <div>spider</div>
* <div>spraycan</div>
* <div>star</div>
* <div>target</div>
* <div>tcross</div>
* <div>top_left_arrow</div>
* <div>top_left_corner</div>
* <div>top_right_corner</div>
* <div>top_side</div>
* <div>top_tee</div>
* <div>trek</div>
* <div>ul_angle</div>
* <div>umbrella</div>
* <div>ur_angle</div>
* <div>watch</div>
* <div>xterm</div>
* </div>
* <table class='widget_list' border=1>
* <tr><td>num_glyphs </td></tr>
* <tr><td>cursor </td></tr>
* <tr><td>arrow </td></tr>
* <tr><td>based_arrow_down </td></tr>
* <tr><td>based_arrow_up </td></tr>
* <tr><td>boat </td></tr>
* <tr><td>bogosity </td></tr>
* <tr><td>bottom_left_corner </td></tr>
* <tr><td>bottom_right_corner </td></tr>
* <tr><td>bottom_side </td></tr>
* <tr><td>bottom_tee </td></tr>
* <tr><td>box_spiral </td></tr>
* <tr><td>center_ptr </td></tr>
* <tr><td>circle </td></tr>
* <tr><td>clock </td></tr>
* <tr><td>coffee_mug </td></tr>
* <tr><td>cross </td></tr>
* <tr><td>cross_reverse </td></tr>
* <tr><td>crosshair </td></tr>
* <tr><td>diamond_cross </td></tr>
* <tr><td>dot </td></tr>
* <tr><td>dotbox </td></tr>
* <tr><td>double_arrow </td></tr>
* <tr><td>draft_large </td></tr>
* <tr><td>draft_small </td></tr>
* <tr><td>draped_box </td></tr>
* <tr><td>exchange </td></tr>
* <tr><td>fleur </td></tr>
* <tr><td>gobbler </td></tr>
* <tr><td>gumby </td></tr>
* <tr><td>hand </td></tr>
* <tr><td>hand </td></tr>
* <tr><td>heart </td></tr>
* <tr><td>icon </td></tr>
* <tr><td>iron_cross </td></tr>
* <tr><td>left_ptr </td></tr>
* <tr><td>left_side </td></tr>
* <tr><td>left_tee </td></tr>
* <tr><td>leftbutton </td></tr>
* <tr><td>ll_angle </td></tr>
* <tr><td>lr_angle </td></tr>
* <tr><td>man </td></tr>
* <tr><td>middlebutton </td></tr>
* <tr><td>mouse </td></tr>
* <tr><td>pencil </td></tr>
* <tr><td>pirate </td></tr>
* <tr><td>plus </td></tr>
* <tr><td>question_arrow </td></tr>
* <tr><td>right_ptr </td></tr>
* <tr><td>right_side </td></tr>
* <tr><td>right_tee </td></tr>
* <tr><td>rightbutton </td></tr>
* <tr><td>rtl_logo </td></tr>
* <tr><td>sailboat </td></tr>
* <tr><td>sb_down_arrow </td></tr>
* <tr><td>sb_h_double_arrow </td></tr>
* <tr><td>sb_left_arrow </td></tr>
* <tr><td>sb_right_arrow </td></tr>
* <tr><td>sb_up_arrow </td></tr>
* <tr><td>sb_v_double_arrow </td></tr>
* <tr><td>shuttle </td></tr>
* <tr><td>sizing </td></tr>
* <tr><td>spider </td></tr>
* <tr><td>spraycan </td></tr>
* <tr><td>star </td></tr>
* <tr><td>target </td></tr>
* <tr><td>tcross </td></tr>
* <tr><td>top_left_arrow </td></tr>
* <tr><td>top_left_corner </td></tr>
* <tr><td>top_right_corner </td></tr>
* <tr><td>top_side </td></tr>
* <tr><td>top_tee </td></tr>
* <tr><td>trek </td></tr>
* <tr><td>ul_angle </td></tr>
* <tr><td>umbrella </td></tr>
* <tr><td>ur_angle </td></tr>
* <tr><td>watch </td></tr>
* <tr><td>xterm </td></tr>
* </table>

View File

@ -1,25 +1,24 @@
--- Set a widget at a specific index, replacing the current one.
--- Set a widget at a specific index, replace the current one.
--
-- @tparam number index A widget or a widget index
-- @tparam widget widget2 The widget to replace the previous one with
-- @treturn boolean Returns `true` if the widget was replaced successfully,
-- `false` otherwise.
-- @tparam widget widget2 The widget to take the place of the first one
-- @treturn boolean If the operation is successful
-- @method set
-- @emits widget::replaced
-- @emitstparam widget::replaced widget self The layout.
-- @emitstparam widget::replaced widget widget The inserted widget.
-- @emitstparam widget::replaced widget widget index The inserted widget.
-- @emitstparam widget::replaced widget previous The previous widget.
-- @emitstparam widget::replaced number index The replaced index.
-- @interface layout
--- Replace the first instance of `widget` in the layout with `widget2`.
--
-- **Signal:** widget::replaced The argument is the new widget and the old one
-- and the index.
-- @tparam widget widget The widget to replace
-- @tparam widget widget2 The widget to replace `widget` with
-- @tparam[opt=false] boolean recursive Recurse into all compatible layouts to
-- find the widget.
-- @treturn boolean Returns `true` if the widget was replaced successfully,
-- `false` otherwise.
-- @tparam[opt=false] boolean recursive Dig in all compatible layouts to find the widget.
-- @treturn boolean If the operation is successful
-- @method replace_widget
-- @emits widget::replaced
-- @emitstparam widget::replaced widget self The layout.
@ -32,44 +31,35 @@
--
-- @tparam number index1 The first widget index
-- @tparam number index2 The second widget index
-- @treturn boolean Returns `true` if the widget was replaced successfully,
-- `false` otherwise.
-- @treturn boolean If the operation is successful
-- @method swap
-- @emits widget::swapped
-- @emitstparam widget::swapped widget self The layout.
-- @emitstparam widget::swapped widget widget1 The first widget.
-- @emitstparam widget::swapped widget widget2 The second widget.
-- @emitstparam widget::swapped widget widget1 index The first widget.
-- @emitstparam widget::swapped widget widget2 index The second widget.
-- @emitstparam widget::swapped number index1 The first index.
-- @emitstparam widget::swapped number index1 The second index.
-- @interface layout
--- Swap 2 widgets in a layout.
--
-- If `widget1` is present multiple time, only the first instance is swapped.
--
-- Calls `set` internally, so the signal `widget::replaced` is emitted for both
-- widgets as well.
--
-- If widget1 is present multiple time, only the first instance is swapped
-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes.
-- if the layouts not the same, then only `widget::replaced` will be emitted.
-- @tparam widget widget1 The first widget
-- @tparam widget widget2 The second widget
-- @tparam[opt=false] boolean recursive Recurse into all compatible layouts to
-- find the widget.
-- @treturn boolean Returns `true` if the widget was replaced successfully,
-- `false` otherwise.
-- @tparam[opt=false] boolean recursive Dig in all compatible layouts to find the widget.
-- @treturn boolean If the operation is successful
-- @method swap_widgets
-- @emits widget::swapped
-- @emitstparam widget::swapped widget self The layout.
-- @emitstparam widget::swapped widget widget1 The first widget.
-- @emitstparam widget::swapped widget widget2 The second widget.
-- @emitstparam widget::swapped widget widget1 index The first widget.
-- @emitstparam widget::swapped widget widget2 index The second widget.
-- @emitstparam widget::swapped number index1 The first index.
-- @emitstparam widget::swapped number index1 The second index.
-- @interface layout
-- @see set
--- Reset the layout. This removes all widgets from the layout.
--- Reset a ratio layout. This removes all widgets from the layout.
-- @method reset
-- @emits widget::reset
-- @emitstparam widget::reset widget self The layout.
-- @interface layout
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,28 +0,0 @@
--<table class='widget_list' border=1>
-- <tr>
-- <th align='center'>Format</th>
-- <th align='center'>Description</th>
-- </tr>
-- <tr><td>%a</td><td>The abbreviated weekday name according to the current locale</td></tr>
-- <tr><td>%A</td><td>the full weekday name according to the current locale</td></tr>
-- <tr><td>%b</td><td>The abbreviated month name according to the current locale</td></tr>
-- <tr><td>%B</td><td>The full month name according to the current locale</td></tr>
-- <tr><td>%d</td><td>The day of the month as a decimal number (range 01 to 31)</td></tr>
-- <tr><td>%e</td><td>The day of the month as a decimal number (range 1 to 31)</td></tr>
-- <tr><td>%F</td><td>Equivalent to %Y-%m-%d (the ISO 8601 date format)</td></tr>
-- <tr><td>%H</td><td>The hour as a decimal number using a 24-hour clock (range 00 to 23)</td></tr>
-- <tr><td>%I</td><td>The hour as a decimal number using a 12-hour clock (range 01 to 12)</td></tr>
-- <tr><td>%k</td><td>The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank</td></tr>
-- <tr><td>%l</td><td>The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank</td></tr>
-- <tr><td>%m</td><td>The month as a decimal number (range 01 to 12)</td></tr>
-- <tr><td>%M</td><td>The minute as a decimal number (range 00 to 59)</td></tr>
-- <tr><td>%p</td><td>Either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale. Noon is treated as "PM" and midnight as "AM".</td></tr>
-- <tr><td>%P</td><td>Like %p but lowercase: "am" or "pm" or a corresponding string for the current locale</td></tr>
-- <tr><td>%r</td><td>The time in a.m. or p.m. notation</td></tr>
-- <tr><td>%R</td><td>The time in 24-hour notation (%H:%M)</td></tr>
-- <tr><td>%S</td><td>The second as a decimal number (range 00 to 60)</td></tr>
-- <tr><td>%T</td><td>The time in 24-hour notation with seconds (%H:%M:%S)</td></tr>
-- <tr><td>%y</td><td>The year as a decimal number without the century</td></tr>
-- <tr><td>%Y</td><td>The year as a decimal number including the century</td></tr>
-- <tr><td>%%</td><td>A literal % character</td></tr>
-- </table>

View File

@ -37,16 +37,16 @@ should be useful for you.
<div class="index_guides">
<div>
@{07-my-first-awesome.md|Getting started}
@{90-FAQ.md|FAQ}
@{01-readme.md|Read Me}
@{89-NEWS.md|NEWS}
<a href="../doc/documentation/07-my-first-awesome.md.html">Getting started</a>
<a href="../doc/documentation/90-FAQ.md.html">FAQ</a>
<a href="../doc/documentation/01-readme.md.html">Read me</a>
<a href="../doc/documentation/89-NEWS.md.html">NEWS</a>
</div>
<div>
@{03-declarative-layout.md|The widget system}
@{09-options.md|Startup options}
@{05-awesomerc.md|The default rc.lua}
@{08-client-layout-system.md|Window management}
<a href="../doc/documentation/03-declarative-layout.md.html">The widget system</a>
<a href="../doc/documentation/09-options.md.html">Startup options</a>
<a href="../doc/documentation/05-awesomerc.md.html">The default rc.lua</a>
<a href="../doc/documentation/08-client-layout-system.md.html">Window management</a>
</div>
</div>
@ -456,25 +456,6 @@ add_custom_tag {
},
}
-- Define the supermodule class.
-- This tag should be used at the module level. All properties from the
-- supermodule will be recursively added to the module by our ldoc template.
-- @supermodule supermodule
add_custom_tag {
name = "supermodule",
hidden = true,
auto_subtags = false
}
-- Mark the item ad hidden.
-- This tag should be used to hide items from the documentation.
-- @hidden
add_custom_tag {
name = "hidden",
hidden = true,
auto_subtags = false
}
-- More fitting section names
kind_names={topic='Documentation', module='Libraries', script='Sample files'}

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -257,23 +257,6 @@ table th, table td {
border-bottom-width: 1px;
}
.flex-list {
display: flex;
flex-wrap: wrap;
}
.flex-list > div {
flex-grow: 1;
/* Use base width based on font size
to make sure text fits when zooming */
width: 9em;
text-align: center;
padding: 10px;
margin: 5px;
border: 1px solid rgb(193, 204, 228);
}
#about {
padding: 15px;
padding-left: 16em;
@ -520,32 +503,3 @@ pre .url { color: #272fc2; text-decoration: underline; }
.index_guides div a:hover {
background-color: #99b3ec;
}
/* Inheritance diagram */
.inheritance .inheritance__level {
list-style: none;
}
.inheritance .inheritance__level--root {
padding-left: 0;
}
.inheritance .inheritance__level__node::before {
content: "↳";
}
.inheritance .inheritance__level__node--root::before {
/* simulate the spacing of the arrow character */
content: " ";
white-space: pre;
}
.extra-header {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.extra-header__section {
flex-grow: 1;
}

View File

@ -38,86 +38,11 @@
# local use_li = ldoc.use_li
# local display_name = ldoc.display_name
# local iter = ldoc.modules.iter
# local function un_cmake(s) return s:gsub("&#59", ";"):gsub("&#34", '"') end
# local function M(txt,item) return ldoc.markup(txt and un_cmake(txt) or nil,item,ldoc.plain) end
# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end
# local nowrap = ldoc.wrap and '' or 'nowrap'
# local html_space = function(s) return s:gsub(" ", "%%20") end
# local no_underscores = function(s) return s:gsub("_", " ") end
# --------- modules hierarchy -------------
# local hierarchy = {}
# local curr = module
# while curr do
# hierarchy[#hierarchy + 1] = curr
# -- no need to do anything more if there is no explicite @supermodule
# if not curr.tags.supermodule then break end
# local super = curr.tags.supermodule[1] -- only consider one way inheritance
# local found = false
# for kind, mods, type in ldoc.kinds() do
# for mod in mods() do
# local name = display_name(mod)
# if name == super then
# curr = mod
# found = true
# end
# if found then break end
# end
# if found then break end
# end
# if not found then curr = nil end
# end
# --------- merge modules content with supermodules -------------
# local all_module_kinds = {}
# if module then
# for kind,items in module.kinds() do
# local myitems = {}
# for item in items() do
# myitems[#myitems + 1] = item
# end
# all_module_kinds[#all_module_kinds + 1] = { kind = kind, items = myitems }
# end
# local filtered_kinds = { "Constructors", "Static module functions",
# "Functions", "Methods", "lib.gears.object.properties Functions" }
# for supermodule in iter(hierarchy) do
# for kind,items in supermodule.kinds() do
# local ignored = false
# for _,filtered in ldoc.pairs(filtered_kinds) do
# if kind == filtered then
# ignored = true
# break
# end
# end
# if not ignored then
# local curr_kind = nil
# for k in iter(all_module_kinds) do
# if k.kind == kind then
# curr_kind = k
# break
# end
# end
# if not curr_kind then
# curr_kind = { kind = kind, items = {} }
# all_module_kinds[#all_module_kinds + 1] = curr_kind
# end
# for item in items() do
# local tobeadded = true
# for i in iter(curr_kind.items) do
# if item.name == i.name then
# tobeadded = false
# break
# end
# end
# if tobeadded then
# item.inherited = true -- force inherited status
# curr_kind.items[#curr_kind.items + 1] = item
# end
# end
# end
# end
# end
# end
<!-- Menu -->
<div id="navigation">
@ -134,8 +59,7 @@
# if module and not ldoc.no_summary and #module.items > 0 then
<h2>Contents</h2>
<ul>
# for k in iter(all_module_kinds) do
# local kind = k.kind
# for kind,items in module.kinds() do
# if not kind:match("^ldoc_skip") then
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
# end
@ -185,90 +109,41 @@
<h1>Module: <code>$(module.name)</code></h1>
<p>$(M(module.summary,module))</p>
<p>$(M(module.description,module))</p>
# if module.tags.include then
$(M(ldoc.include_file(module.tags.include)))
# end
# if module.see then
# local li,il = use_li(module.see)
<h3>See also:</h3>
<ul>
# for see in iter(module.see) do
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
# end -- for
</ul>
# end -- if see
# if module.usage then
# local li,il = use_li(module.usage)
<h3>Usage:</h3>
<ul>
# for usage in iter(module.usage) do
$(li)<pre class="example">$(ldoc.escape(un_cmake(usage)))</pre>$(il)
$(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il)
# end -- for
</ul>
# end -- if usage
<div class="extra-header">
# if module.tags.supermodule then
<div class="extra-header__section">
<h3>Class Hierarchy</h3>
<div class="inheritance">
# local function draw_hierary_recursifly(i)
# local is_root_level = (i == #hierarchy)
<ul class="inheritance__level $(is_root_level and 'inheritance__level--root' or '')">
<li class="inheritance__level__node $(is_root_level and 'inheritance__level__node--root' or '')">
# local mod = hierarchy[i]
# local name = display_name(hierarchy[i])
# if mod == module then
<strong>$(name)</strong>
# else
<a href="$(ldoc.ref_to_module(mod))">$(name)</a>
# end
</li>
# if i > 1 then
<li>
# draw_hierary_recursifly(i - 1)
</li>
# end
</ul>
# end -- function draw_hierary_recursifly
# draw_hierary_recursifly(#hierarchy)
</div>
</div>
# end -- module.tags.supermodule
# if module.tags.include then
$(M(ldoc.include_file(module.tags.include)))
# end
# if module.info then
<div class="extra-header__section">
<h3>Info:</h3>
<ul>
# for tag, value in module.info:iter() do
# if tag == 'Author' then
<li>
<strong>Originally authored by</strong>: $(M(value,module))<br />
<small>(Full contributors list available on
<a href="https://github.com/awesomeWM/awesome/graphs/contributors">
our github project)
</a></small>
</li>
# else
<li><strong>$(tag)</strong>: $(M(value,module))</li>
# end
# end
</ul>
</div>
# end -- if module.info
# if module.see then
<div class="extra-header__section">
# local li,il = use_li(module.see)
# local list_or_p =(#module.see > 1) and 'ul' or 'p'
<h3>See also:</h3>
<$(list_or_p)>
# for see in iter(module.see) do
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
# end -- for
</$(list_or_p)>
</div>
# end -- if module.see
</div>
# if not ldoc.no_summary then
# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
# local last_kind = ""
# for k in iter(all_module_kinds) do
# local kind = k.kind
# for kind,items in module.kinds() do
# if not kind:match("^ldoc_skip") then
# if last_kind ~= "" then
</table>
@ -276,9 +151,8 @@
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
<table class="function_list">
# end
# for item in iter(k.items) do if not item.tags.hidden then
# for item in items() do
# local dn = display_name(item)
# local inherited = (item.baseclass ~= module.name)
# if item.sanitize_type then item.sanitize_type(item, ldoc) end
<tr>
# if item.display_type and not item.compact_signature then
@ -292,14 +166,14 @@
# end
</td>
# end
<td colspan="$(inherited and 1 or 2)" class="summary">$(M(item.summary,item))</td>
# if inherited then
<td class="baseclass" nowrap>
<td colspan="$(item.inherited and 1 or 2)" class="summary">$(M(item.summary,item))</td>
# if item.inherited then
<td class="baseclass" $(nowrap)>
Inherited from $(item.baseclass)
</td>
# end
</tr>
# end end -- for items
# end -- for items
# last_kind = kind
#end -- for kinds
</table>
@ -313,8 +187,7 @@
# --- function parameters or table fields.
# local show_return = not ldoc.no_return_or_parms
# local show_parms, last_kind = show_return, ""
# for k in iter(all_module_kinds) do
# local kind = k.kind
# for kind, items in module.kinds() do
# local kitem = module.kinds:get_item(kind)
# local has_description = kitem and ldoc.descript(kitem) ~= ""
# if not kind:match("^ldoc_skip") then
@ -332,13 +205,13 @@
# end
# if kitem.usage then
<h3>Usage:</h3>
<pre class="example">$(ldoc.prettify(un_cmake(kitem.usage[1])))</pre>
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre>
# end
# end
# if not kind:match("^ldoc_skip") then
<dl class="function">
# end
# for item in iter(k.items) do if not item.tags.hidden then
# for item in items() do
<dt>
<a name = "$(item.name)"></a>
<strong>$(display_name(item))</strong>
@ -352,8 +225,8 @@
<span class="proptype">$(item.display_type)</span>
# end
<span class="baseclass" $(nowrap)>
# if item.baseclass ~= module.name then
&middot;&nbsp;Inherited from $(M(item.baseclass, item))
# if item.inherited then
&middot;&nbsp;Inherited from $(item.baseclass)
# end
# if item.extra_summary then
# for _, col in ldoc.ipairs(item.extra_summary) do
@ -451,7 +324,7 @@
<h3>Usage:</h3>
<ul>
# for usage in iter(item.usage) do
$(li)<pre class="example">$(ldoc.prettify(un_cmake(usage)))</pre>$(il)
$(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il)
# end -- for
</ul>
# end -- if usage
@ -498,7 +371,7 @@
</span>
</dd>
# end end -- for items
# end -- for items
# last_kind = kind
# end -- for kinds
</dl>

2
ewmh.c
View File

@ -468,7 +468,7 @@ ewmh_process_client_message(xcb_client_message_event_t *ev)
lua_State *L = globalconf_get_lua_State();
luaA_object_push(L, globalconf.tags.tab[idx]);
lua_pushstring(L, "ewmh");
luaA_object_emit_signal(L, -2, "request::select", 1);
luaA_object_emit_signal(L, -1, "request::select", 1);
lua_pop(L, 1);
}
}

View File

@ -58,28 +58,6 @@ do
assert(root.object == root_object)
end
--- The old wallpaper only took native surfaces.
--
-- This was a problem for the test backend. The new function takes both
-- native surfaces and LGI-ified Cairo surfaces.
function root.wallpaper(pattern)
if not pattern then return root._wallpaper() end
-- Checking for type will either potentially `error()` or always
-- return `userdata`. This check will error() when the surface is
-- already native.
local err = pcall(function() return pattern._native end)
-- The presence of `root._write_string` means the test backend is
-- used. Avoid passing the native surface.
if err and not root._write_string then
return root._wallpaper(pattern._native)
else
return root._wallpaper(pattern)
end
end
-- root.bottons() used to be a capi function. However this proved confusing
-- as rc.lua used `awful.button` and `root.buttons()` used capi.button. There
-- was a little documented hack to "flatten" awful.button into a pair of

View File

@ -595,42 +595,18 @@ function client.object.move_to_screen(self, s)
end
end
--- Find suitable tags for newly created clients.
--
-- In most cases, the functionality you're actually looking for as a user will
-- either be
--
-- c:tags(c.screen.selected_tags)
--
-- or
--
-- local s = awful.screen.focused()
-- c:move_to_screen(s)
-- c:tags(s.selected_tags)
--
-- Despite its naming, this is primarily used to tag newly created clients.
-- As such, this method has no effect when applied to a client that already has
-- tags assigned (except for emitting `property::tag`).
--
-- Additionally, while it is a rare case, if the client's screen has no selected
-- tags at the point of calling this method, it will fall back to the screen's
-- full set of tags.
--
--- Tag a client with the set of current tags.
-- @method to_selected_tags
-- @see screen.selected_tags
function client.object.to_selected_tags(self)
local tags = {}
-- From the client's current tags, find the ones that
-- belong to the client's screen
for _, t in ipairs(self:tags()) do
if get_screen(t.screen) == get_screen(self.screen) then
table.insert(tags, t)
end
end
-- If no tags were found,
-- choose the screen's selected tags, if any, or all of the screens tags
if self.screen then
if #tags == 0 then
tags = self.screen.selected_tags
@ -641,7 +617,6 @@ function client.object.to_selected_tags(self)
end
end
-- Prevent clients from becoming untagged
if #tags ~= 0 then
self:tags(tags)
end
@ -1159,13 +1134,10 @@ end
--- Change window factor of a client.
--
-- This will emit `property::windowfact` on the specific tag object
-- `c.screen.selected_tag`.
--
-- @legacylayout awful.client.incwfact
-- @tparam number add Amount to increase/decrease the client's window factor by.
-- @tparam number add Amount to increase/decrease the client's window factor.
-- Should be between `-current_window_factor` and something close to
-- infinite. Normalisation then ensures that the sum of all factors is 1.
-- infinite. The normalisation then ensures that the sum of all factors is 1.
-- @tparam client c the client.
-- @emits property::windowfact
function client.incwfact(add, c)

View File

@ -196,7 +196,7 @@ function focus.global_bydirection(dir, c, stacked)
local scr = get_screen(sel and sel.screen or screen.focused())
-- change focus inside the screen
focus.bydirection(dir, sel, stacked)
focus.bydirection(dir, sel)
-- if focus not changed, we must change screen
if sel == capi.client.focus then

View File

@ -1,8 +1,8 @@
---------------------------------------------------------------------------
--- D-Bus module for awful.
--
-- This module simply requests the org.awesomewm.awful name on the D-Bus
-- for future usage by other awful modules.
-- This module simply request the org.awesomewm.awful name on the D-Bus
-- for futur usage by other awful modules.
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou

View File

@ -438,10 +438,7 @@ function widget.new(args)
end
function widget_instance:_create_group_columns(column_layouts, group, keys, s, wibox_height)
local line_height = math.max(
beautiful.get_font_height(self.font),
beautiful.get_font_height(self.description_font)
)
local line_height = beautiful.get_font_height(self.font)
local group_label_height = line_height + self.group_margin
-- -1 for possible pagination:
local max_height_px = wibox_height - group_label_height
@ -475,7 +472,7 @@ function widget.new(args)
table.insert(((i<available_height_items) and new_keys or overlap_leftovers), keys[i])
end
keys = new_keys
table.insert(keys, {key="", description=""})
table.insert(keys, {key=markup.fg(self.modifiers_fg, ""), description=""})
end
if not current_column then
current_column = {layout=wibox.layout.fixed.vertical()}
@ -484,39 +481,32 @@ function widget.new(args)
local function insert_keys(_keys, _add_new_column)
local max_label_width = 0
local max_label_content = ""
local joined_labels = ""
for i, key in ipairs(_keys) do
local length = string.len(key.key or '') + string.len(key.description or '')
local modifiers = key.mod
if not modifiers or modifiers == "none" then
modifiers = ""
else
length = length + string.len(modifiers) + 1 -- +1 for "+" character
modifiers = markup.fg(self.modifiers_fg, modifiers.."+")
end
local key_label = ""
if key.keylist and #key.keylist > 1 then
for each_key_idx, each_key in ipairs(key.keylist) do
key_label = key_label .. gstring.xml_escape(each_key)
if each_key_idx ~= #key.keylist then
key_label = key_label .. markup.fg(self.modifiers_fg, '/')
end
end
elseif key.key then
key_label = gstring.xml_escape(key.key)
end
local rendered_hotkey = markup.font(self.font,
modifiers .. key_label .. " "
modifiers .. (key.key or "") .. " "
) .. markup.font(self.description_font,
key.description or ""
)
local label_width = wibox.widget.textbox.get_markup_geometry(rendered_hotkey, s).width
if label_width > max_label_width then
max_label_width = label_width
if length > max_label_width then
max_label_width = length
max_label_content = rendered_hotkey
end
joined_labels = joined_labels .. rendered_hotkey .. (i~=#_keys and "\n" or "")
end
current_column.layout:add(wibox.widget.textbox(joined_labels))
local max_width = max_label_width + self.group_margin
if not current_column.max_width or (max_width > current_column.max_width) then
local max_width, _ = wibox.widget.textbox(max_label_content):get_preferred_size(s)
max_width = max_width + self.group_margin
if not current_column.max_width or max_width > current_column.max_width then
current_column.max_width = max_width
end
-- +1 for group label:

View File

@ -35,7 +35,6 @@ local ret = {
tooltip = require("awful.tooltip");
permissions = require("awful.permissions");
titlebar = require("awful.titlebar");
wallpaper = require("awful.wallpaper");
rules = require("awful.rules");
popup = require("awful.popup");
spawn = require("awful.spawn");

View File

@ -1,27 +1,6 @@
---------------------------------------------------------------------------
--- Create easily new key objects ignoring certain modifiers.
--
-- A key object can be used by @{awful.keyboard} and @{client} to define
-- keybindings.
--
-- Use awful.key to define a keybinding
-- ---
--
-- This example shows how to define a basic key object:
--
-- @DOC_text_awful_key_constructor_default_EXAMPLE@
--
-- This example shows how to define the same basic key object with the
-- declarative pattern:
--
-- @DOC_text_awful_key_constructor_declarative_EXAMPLE@
--
-- This second example of a key definition uses the numrow keygroup. In this
-- example, we define a key object, that select the tag to show according to
-- the key index from the numrow.
--
-- @DOC_text_awful_key_constructor_keygroup_EXAMPLE@
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2018 Emmanuel Lepage Vallee
@ -45,6 +24,21 @@ local gobject = require("gears.object")
-- @property key
-- @param string
--- A group of keys.
--
-- The valid keygroups are:
--
-- * **numrow**: The row above the letters in the US PC-105/PC-104 keyboards
-- and its derivative. This is usually the number 1-9 followed by 0.
-- * **arrows**: The Left/Right/Top/Bottom keys usually located right of the
-- spacebar.
-- * **fkeys**: The keys F1 through F12 located at the topmost row of any
-- keyboard, plus F13 through F35 on specialist keyboards.
-- * **numpad**: The number keys on the keypad to the right of the letters and
-- the arrow keys. Not present in every keyboard.
--
-- @property keygroup
--- The table of modifier keys.
--
-- A modifier, such as `Control` are a predetermined set of keys that can be
@ -105,32 +99,6 @@ local key = { mt = {}, hotkeys = {} }
local reverse_map = setmetatable({}, {__mode="k"})
--- The keygroups names.
--
-- It can be used instead of keygroup names.
--
-- Values associated to each property of this table are string:
--
-- - **NUMROW** = `"numrow"`: The row above the letters in the US PC-105/PC-104 keyboards and
-- its derivative. This is usually the number 1-9 followed by 0.
--
-- - **ARROWS** = `"arrows"`: The Left/Right/Top/Bottom keys usually located right of the
-- spacebar.
--
-- - **FKEYS** = `"fkeys"`: The keys F1 through F12 located at the topmost row of any
-- keyboard, plus F13 through F35 on specialist keyboards.
--
-- - **NUMPAD** = `"numpad"`: The number keys on the keypad to the right of the letters and
-- the arrow keys. Not present in every keyboard.
--
-- @table keygroup
key.keygroup = {
NUMROW = 'numrow', -- The number row.
ARROWS = 'arrows', -- The directionnal arrows.
FKEYS = 'fkeys', -- The function keys.
NUMPAD = 'numpad', -- The numpad keys.
}
function key:set_key(k)
for _, v in ipairs(self) do
v.key = k
@ -248,13 +216,11 @@ end
--
-- @constructorfct2 awful.key
-- @tparam table args
-- @tparam string args.key The key to trigger an event. It can be the character
-- itself of `#+keycode`.
-- @tparam[opt] string args.keygroup The keygroup to trigger an event. This
-- parameter must be used as a replacement for the `key` parameter. See
-- @{awful.key.keygroup}.
-- @tparam table args.modifiers A list of modifier keys. Valid modifiers are:
-- @tparam function args.key The key to trigger an event. It can be the character
-- itself of `#+keycode` (**mandatory**).
-- @tparam function args.modifiers A list of modifier keys. Valid modifiers are:
-- `Any`, `Mod1`, Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
-- This argument is (**mandatory**).
-- @tparam function args.on_press Callback for when the key is pressed.
-- @tparam function args.on_release Callback for when the key is released.

View File

@ -474,20 +474,12 @@ function keygrabber:start()
end
--- Stop the keygrabber.
--
-- Also stops any `timeout`.
--
-- @method stop
-- @emits stopped
-- @emits property::current_instance
function keygrabber:stop(_stop_key, _stop_mods) -- (at)function disables ldoc params
keygrab.stop(self.grabber)
local timer = self._private.timer
if timer and timer.started then
timer:stop()
end
if self.stop_callback then
self.stop_callback(
self.current_instance, _stop_key, _stop_mods, self.sequence
@ -714,13 +706,12 @@ function keygrab.run_with_keybindings(args)
else
gdebug.print_warning(
"The hook's 3rd parameter has to be a function. " ..
gdebug.dump_return(v or {})
gdebug.dump(v or {})
)
end
else
gdebug.print_warning(
"The keybindings should be awful.key objects" ..
gdebug.dump_return(v or {})
"The keybindings should be awful.key objects".. gdebug.dump(v or {})
)
end
end

View File

@ -35,29 +35,28 @@ mouse.wibox = {}
--
-- @DOC_screen_client_snap_EXAMPLE@
--
-- @tfield integer snap.default_distance
-- @tfield integer awful.mouse.snap.default_distance
-- @tparam[opt=8] integer default_distance
-- @see awful.mouse.snap
--- The default distance before activating screen edge snap.
-- @tfield integer snap.aerosnap_distance
-- @tparam[opt=16] integer aerosnap_distance
-- @tfield integer awful.mouse.snap.aerosnap_distance
-- @tparam[opt=16] integer default_distance
-- @see awful.mouse.snap
--- Enable screen edges snapping.
--
--
--
--@DOC_awful_placement_aero_snap_EXAMPLE@
--
-- @tfield[opt=true] boolean snap.edge_enabled
-- @tparam boolean edge_enabled
-- @tfield[opt=true] boolean awful.mouse.snap.edge_enabled
--- Enable client to client snapping.
-- @tfield[opt=true] boolean snap.client_enabled
-- @tparam boolean client_enabled
-- @tfield[opt=true] boolean awful.mouse.snap.client_enabled
--- Enable changing tag when a client is dragged to the edge of the screen.
-- @tfield[opt=false] boolean drag_to_tag.enabled
-- @tparam boolean enabled
-- @tfield[opt=false] integer awful.mouse.drag_to_tag.enabled
--- The snap outline background color.
-- @beautiful beautiful.snap_bg
@ -342,9 +341,9 @@ function mouse.remove_client_mousebinding(button)
return false
end
for k, b in ipairs {"left", "middle", "right"} do
for _, b in ipairs {"left", "right", "middle"} do
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
return capi.mouse.coords().buttons[k]
return capi.mouse.coords().buttons[1]
end
end
@ -368,10 +367,6 @@ end)
capi.mouse.set_index_miss_handler(function(_,key)
if mouse.object["get_"..key] then
return mouse.object["get_"..key]()
elseif mouse.object[key] and key:sub(1, 3) == "is_" then
return mouse.object[key]()
elseif mouse.object[key] then
return mouse.object[key]
else
return props[key]
end

View File

@ -184,10 +184,6 @@ local function compose(...)
attach(d, ret, args)
end
-- Cleanup the override because otherwise it might leak into
-- future calls.
rawset(args, "override_geometry", nil)
return last_geo, rets
end, "compose")
@ -280,30 +276,6 @@ local outer_positions = {
bottom_middle = function(r, w, _) return {x=r.x-w/2+r.width/2, y=r.y }, "middle" end,
}
-- Map the opposite side for a string
local opposites = {
top = "bottom",
bottom = "top",
left = "right",
right = "left",
width = "height",
height = "width",
x = "y",
y = "x",
}
-- List reletvant sides for each orientation.
local struts_orientation_to_sides = {
horizontal = { "top" , "bottom" },
vertical = { "left", "right" }
}
-- Map orientation to the length components (width/height).
local orientation_to_length = {
horizontal = "width",
vertical = "height"
}
--- Add a context to the arguments.
-- This function extend the argument table. The context is used by some
-- internal helper methods. If there already is a context, it has priority and
@ -506,7 +478,7 @@ wibox_update_strut = function(d, position, args)
-- Detect horizontal or vertical drawables
local geo = area_common(d)
local orientation = geo.width < geo.height and "vertical" or "horizontal"
local vertical = geo.width < geo.height
-- Look into the `position` string to find the relevants sides to crop from
-- the workarea
@ -514,12 +486,17 @@ wibox_update_strut = function(d, position, args)
local m = get_decoration(args)
for _, v in ipairs(struts_orientation_to_sides[orientation]) do
if vertical then
for _, v in ipairs {"right", "left"} do
if (not position) or position:match(v) then
-- Add the "short" rectangle lenght then the above and below margins.
struts[v] = geo[opposites[orientation_to_length[orientation]]]
+ m[v]
+ m[opposites[v]]
struts[v] = geo.width + m[v]
end
end
else
for _, v in ipairs {"top", "bottom"} do
if (not position) or position:match(v) then
struts[v] = geo.height + m[v]
end
end
end

View File

@ -19,7 +19,6 @@
-- @author Emmanuel Lepage Vallee
-- @copyright 2016 Emmanuel Lepage Vallee
-- @popupmod awful.popup
-- @supermodule wibox
---------------------------------------------------------------------------
local wibox = require( "wibox" )
local gtable = require( "gears.table" )

View File

@ -114,6 +114,7 @@ local io = io
local table = table
local math = math
local ipairs = ipairs
local pcall = pcall
local capi =
{
selection = selection
@ -257,11 +258,6 @@ local function history_add(id, command)
end
local function have_multibyte_char_at(text, position)
return text:sub(position, position):wlen() == -1
end
--- Draw the prompt text with a cursor.
-- @tparam table args The table of arguments.
-- @field text The text.
@ -289,14 +285,10 @@ local function prompt_text_with_cursor(args)
text_start = gstring.xml_escape(text)
text_end = ""
else
local offset = 0
if have_multibyte_char_at(text, args.cursor_pos) then
offset = 1
end
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos + offset))
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos))
spacer = " "
text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1))
text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1 + offset))
text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1))
end
local cursor_color = gcolor.ensure_pango_color(args.cursor_color)
@ -671,9 +663,6 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
elseif key == "b" then
if cur_pos > 1 then
cur_pos = cur_pos - 1
if have_multibyte_char_at(command, cur_pos) then
cur_pos = cur_pos - 1
end
end
elseif key == "d" then
if cur_pos <= #command then
@ -722,20 +711,12 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
end
elseif key == "f" then
if cur_pos <= #command then
if have_multibyte_char_at(command, cur_pos) then
cur_pos = cur_pos + 2
else
cur_pos = cur_pos + 1
end
end
elseif key == "h" then
if cur_pos > 1 then
local offset = 0
if have_multibyte_char_at(command, cur_pos - 1) then
offset = 1
end
command = command:sub(1, cur_pos - 2 - offset) .. command:sub(cur_pos)
cur_pos = cur_pos - 1 - offset
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
end
elseif key == "k" then
command = command:sub(1, cur_pos - 1)
@ -863,12 +844,8 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
cur_pos = #command + 1
elseif key == "BackSpace" then
if cur_pos > 1 then
local offset = 0
if have_multibyte_char_at(command, cur_pos - 1) then
offset = 1
end
command = command:sub(1, cur_pos - 2 - offset) .. command:sub(cur_pos)
cur_pos = cur_pos - 1 - offset
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
end
elseif key == "Delete" then
command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
@ -912,7 +889,22 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
selectall = nil
end
update()
local success = pcall(update)
while not success do
-- TODO UGLY HACK TODO
-- Setting the text failed. Most likely reason is that the user
-- entered a multibyte character and pressed backspace which only
-- removed the last byte. Let's remove another byte.
if cur_pos <= 1 then
-- No text left?!
break
end
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
success = pcall(update)
end
if changed_callback then
changed_callback(command)
end

View File

@ -224,56 +224,15 @@ local capi =
client = client,
}
local lgi = require("lgi")
local Gio = lgi.Gio
local GLib = lgi.GLib
local util = require("awful.util")
local gtable = require("gears.table")
local gtimer = require("gears.timer")
local aclient = require("awful.client")
local protected_call = require("gears.protected_call")
local watcher = require("gears.watcher")
local spawn = {}
local end_of_file
do
-- API changes, bug fixes and lots of fun. Figure out how a EOF is signalled.
local input
if not pcall(function()
-- No idea when this API changed, but some versions expect a string,
-- others a table with some special(?) entries
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data(""))
end) then
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data({}))
end
local line, length = input:read_line()
if not line then
-- Fixed in 2016: NULL on the C side is transformed to nil in Lua
end_of_file = function(arg)
return not arg
end
elseif tostring(line) == "" and #line ~= length then
-- "Historic" behaviour for end-of-file:
-- - NULL is turned into an empty string
-- - The length variable is not initialized
-- It's highly unlikely that the uninitialized variable has value zero.
-- Use this hack to detect EOF.
end_of_file = function(arg1, arg2)
return #arg1 ~= arg2
end
else
assert(tostring(line) == "", "Cannot determine how to detect EOF")
-- The above uninitialized variable was fixed and thus length is
-- always 0 when line is NULL in C. We cannot tell apart an empty line and
-- EOF in this case.
require("gears.debug").print_warning("Cannot reliably detect EOF on an "
.. "GIOInputStream with this LGI version")
end_of_file = function(arg)
return tostring(arg) == ""
end
end
end
local function hash_command(cmd, rules)
rules = rules or {}
cmd = type(cmd) == "string" and cmd or table.concat(cmd, ';')
@ -392,38 +351,10 @@ end
-- @treturn[1] Integer the PID of the forked process.
-- @treturn[2] string Error message.
-- @staticfct awful.spawn.with_line_callback
function spawn.with_line_callback(cmd, callbacks)
local stdout_callback, stderr_callback, done_callback, exit_callback =
callbacks.stdout, callbacks.stderr, callbacks.output_done, callbacks.exit
local have_stdout, have_stderr = stdout_callback ~= nil, stderr_callback ~= nil
local pid, _, stdin, stdout, stderr = capi.awesome.spawn(cmd,
false, false, have_stdout, have_stderr, exit_callback)
if type(pid) == "string" then
-- Error
return pid
end
local done_before = false
local function step_done()
if have_stdout and have_stderr and not done_before then
done_before = true
return
end
if done_callback then
done_callback()
end
end
if have_stdout then
spawn.read_lines(Gio.UnixInputStream.new(stdout, true),
stdout_callback, step_done, true)
end
if have_stderr then
spawn.read_lines(Gio.UnixInputStream.new(stderr, true),
stderr_callback, step_done, true)
end
assert(stdin == nil)
return pid
end
-- It is still "officially" in `awful.spawn`, but since `gears.watcher`
-- depends on it, the implementation lives there.
spawn.with_line_callback = watcher._with_line_callback
--- Asynchronously spawn a program and capture its output.
-- (wraps `spawn.with_line_callback`).
@ -438,43 +369,10 @@ end
-- @treturn[2] string Error message.
-- @see spawn.with_line_callback
-- @staticfct awful.spawn.easy_async
function spawn.easy_async(cmd, callback)
local stdout = ''
local stderr = ''
local exitcode, exitreason
local function parse_stdout(str)
stdout = stdout .. str .. "\n"
end
local function parse_stderr(str)
stderr = stderr .. str .. "\n"
end
local function done_callback()
return callback(stdout, stderr, exitreason, exitcode)
end
local exit_callback_fired = false
local output_done_callback_fired = false
local function exit_callback(reason, code)
exitcode = code
exitreason = reason
exit_callback_fired = true
if output_done_callback_fired then
return done_callback()
end
end
local function output_done_callback()
output_done_callback_fired = true
if exit_callback_fired then
return done_callback()
end
end
return spawn.with_line_callback(
cmd, {
stdout=parse_stdout,
stderr=parse_stderr,
exit=exit_callback,
output_done=output_done_callback
})
end
-- It is still "officially" in `awful.spawn`, but since `gears.watcher`
-- depends on it, the implementation lives there.
spawn.easy_async = watcher._easy_async
--- Call `spawn.easy_async` with a shell.
-- This calls `cmd` with `$SHELL -c` (via `awful.util.shell`).
@ -501,42 +399,8 @@ end
-- operation finishes (e.g. due to end of file).
-- @tparam[opt=false] boolean close Should the stream be closed after end-of-file?
-- @staticfct awful.spawn.read_lines
function spawn.read_lines(input_stream, line_callback, done_callback, close)
local stream = Gio.DataInputStream.new(input_stream)
local function done()
if close then
stream:close()
end
stream:set_buffer_size(0)
if done_callback then
protected_call(done_callback)
end
end
local start_read, finish_read
start_read = function()
stream:read_line_async(GLib.PRIORITY_DEFAULT, nil, finish_read)
end
finish_read = function(obj, res)
local line, length = obj:read_line_finish(res)
if type(length) ~= "number" then
-- Error
print("Error in awful.spawn.read_lines:", tostring(length))
done()
elseif end_of_file(line, length) then
-- End of file
done()
else
-- Read a line
-- This needs tostring() for older lgi versions which returned
-- "GLib.Bytes" instead of Lua strings (I guess)
protected_call(line_callback, tostring(line))
-- Read the next line
start_read()
end
end
start_read()
end
spawn.read_lines = watcher._read_lines
-- When a command should only be executed once or only have a single instance,
-- track the SNID set on them to prevent multiple execution.

View File

@ -349,7 +349,7 @@ function tag.find_fallback(screen, invalids)
end
end
--- Emitted when all clients are removed from the tag.
--- When all clients are removed from the tag.
-- @signal cleared
-- @see clear
@ -1841,25 +1841,20 @@ capi.client.connect_signal("untagged", client_untagged)
capi.client.connect_signal("tagged", client_tagged)
capi.tag.connect_signal("request::select", tag.object.view_only)
--- Emitted when the number of urgent clients on this tag changes.
--- True when a tagged client is urgent
-- @signal property::urgent
-- @param boolean `true` if there is at least one urgent client on the tag.
-- @see client.urgent
--- Emitted when the number of urgent clients on this tag changes.
--- The number of urgent tagged clients
-- @signal property::urgent_count
-- @param integer The number of urgent clients on the tag.
-- @see client.urgent
--- Emitted when a screen is removed.
--
-- This can be used to salvage existing tags by moving them to a new
-- screen (or creating a virtual screen).
--
-- By default, there is no handler for this request and the tags will be deleted.
-- To prevent this, an handler for this request must simply set a new screen
-- screen (or creating a virtual screen). By default, there is no
-- handler for this request. The tags will be deleted. To prevent
-- this, an handler for this request must simply set a new screen
-- for the tag.
--
-- @signal request::screen
-- @tparam string context Why it was called.

View File

@ -505,33 +505,20 @@ local function get_children_by_id(self, name)
end
--- Create a new titlebar for the given client.
--
-- Every client can hold up to four titlebars, one for each side (i.e. each
-- value of `args.position`).
--
-- If this constructor is called again with the same
-- values for the client (`c`) and the titlebar position (`args.position`),
-- the previous titlebar will be removed and replaced by the new one.
--
-- @DOC_awful_titlebar_constructor_EXAMPLE@
--
-- @tparam client c The client the titlebar will be attached to.
--- Get a client's titlebar.
-- @tparam client c The client for which a titlebar is wanted.
-- @tparam[opt={}] table args A table with extra arguments for the titlebar.
-- @tparam[opt=font.height*1.5] number args.size The size of the titlebar. Will
-- be interpreted as `height` for horizontal titlebars or as `width` for
-- vertical titlebars.
-- @tparam[opt="top"] string args.position Possible values are `"top"`,
-- `"left"`, `"right"` and `"bottom"`.
-- @tparam[opt] string args.bg_normal
-- @tparam[opt] string args.bg_focus
-- @tparam[opt] string args.bgimage_normal
-- @tparam[opt] string args.bgimage_focus
-- @tparam[opt] string args.fg_normal
-- @tparam[opt] string args.fg_focus
-- @tparam[opt] string args.font
-- @tparam[opt=font.height*1.5] number args.size The height of the titlebar.
-- @tparam[opt=top] string args.position" values are `top`,
-- `left`, `right` and `bottom`.
-- @tparam[opt=top] string args.bg_normal
-- @tparam[opt=top] string args.bg_focus
-- @tparam[opt=top] string args.bgimage_normal
-- @tparam[opt=top] string args.bgimage_focus
-- @tparam[opt=top] string args.fg_normal
-- @tparam[opt=top] string args.fg_focus
-- @tparam[opt=top] string args.font
-- @constructorfct awful.titlebar
-- @treturn wibox.drawable The newly created titlebar object.
local function new(c, args)
args = args or {}
local position = args.position or "top"
@ -591,10 +578,10 @@ local function new(c, args)
return ret
end
--- Show the client's titlebar.
--- Show a client's titlebar.
-- @param c The client whose titlebar is modified
-- @tparam[opt="top"] string position The position of the titlebar. Must be one of `"left"`,
-- `"right"`, `"top"`, `"bottom"`.
-- @param[opt] position The position of the titlebar. Must be one of "left",
-- "right", "top", "bottom". Default is "top".
-- @staticfct awful.titlebar.show
-- @request client titlebars show granted Called when `awful.titlebar.show` is
-- called.
@ -607,20 +594,20 @@ function titlebar.show(c, position)
new(c, args)
end
--- Hide the client's titlebar.
--- Hide a client's titlebar.
-- @param c The client whose titlebar is modified
-- @tparam[opt="top"] string position The position of the titlebar. Must be one of `"left"`,
-- `"right"`, `"top"`, `"bottom"`.
-- @param[opt] position The position of the titlebar. Must be one of "left",
-- "right", "top", "bottom". Default is "top".
-- @staticfct awful.titlebar.hide
function titlebar.hide(c, position)
position = position or "top"
get_titlebar_function(c, position)(c, 0)
end
--- Toggle the client's titlebar, hiding it if it is visible, otherwise showing it.
--- Toggle a client's titlebar, hiding it if it is visible, otherwise showing it.
-- @param c The client whose titlebar is modified
-- @tparam[opt="top"] string position The position of the titlebar. Must be one of `"left"`,
-- `"right"`, `"top"`, `"bottom"`.
-- @param[opt] position The position of the titlebar. Must be one of "left",
-- "right", "top", "bottom". Default is "top".
-- @staticfct awful.titlebar.toggle
-- @request client titlebars toggle granted Called when `awful.titlebar.toggle` is
-- called.
@ -662,12 +649,9 @@ local function update_on_signal(c, signal, widget)
table.insert(widgets, widget)
end
--- Create a new title widget.
--
-- A title widget displays the name of a client.
--- Create a new titlewidget. A title widget displays the name of a client.
-- Please note that this returns a textbox and all of textbox' API is available.
-- This way, you can e.g. modify the font that is used.
--
-- @param c The client for which a titlewidget should be created.
-- @return The title widget.
-- @constructorfct awful.titlebar.widget.titlewidget
@ -683,12 +667,9 @@ function titlebar.widget.titlewidget(c)
return ret
end
--- Create a new icon widget.
--
-- An icon widget displays the icon of a client.
--- Create a new icon widget. An icon widget displays the icon of a client.
-- Please note that this returns an imagebox and all of the imagebox' API is
-- available. This way, you can e.g. disallow resizes.
--
-- @param c The client for which an icon widget should be created.
-- @return The icon widget.
-- @constructorfct awful.titlebar.widget.iconwidget
@ -696,16 +677,13 @@ function titlebar.widget.iconwidget(c)
return clienticon(c)
end
--- Create a new button widget.
--
-- A button widget displays an image and reacts to
--- Create a new button widget. A button widget displays an image and reacts to
-- mouse clicks. Please note that the caller has to make sure that this widget
-- gets redrawn when needed by calling the returned widget's `:update()` method.
-- gets redrawn when needed by calling the returned widget's update() function.
-- The selector function should return a value describing a state. If the value
-- is a boolean, either `"active"` or `"inactive"` are used. The actual image is
-- then found in the theme as `titlebar_[name]_button_[normal/focus]_[state]`.
-- is a boolean, either "active" or "inactive" are used. The actual image is
-- then found in the theme as "titlebar_[name]_button_[normal/focus]_[state]".
-- If that value does not exist, the focused state is ignored for the next try.
--
-- @param c The client for which a button is created.
-- @tparam string name Name of the button, used for accessing the theme and
-- in the tooltip.

View File

@ -40,7 +40,6 @@
-- @author Sébastien Gross &lt;seb•ɱɩɲʋʃ•awesome•ɑƬ•chezwam•ɖɵʈ•org&gt;
-- @copyright 2009 Sébastien Gross
-- @popupmod awful.tooltip
-- @supermodule wibox
-------------------------------------------------------------------------
local timer = require("gears.timer")

View File

@ -7,7 +7,6 @@
---------------------------------------------------------------------------
-- Grab environment we need
local os = os
local assert = assert
local load = loadstring or load -- luacheck: globals loadstring (compatibility with Lua 5.1)
local loadfile = loadfile
@ -19,6 +18,7 @@ local gstring = require("gears.string")
local grect = require("gears.geometry").rectangle
local gcolor = require("gears.color")
local gfs = require("gears.filesystem")
local watcher = require("gears.watcher")
local capi =
{
awesome = awesome,
@ -31,8 +31,7 @@ local util = {}
util.table = {}
--- The default shell used when spawing processes.
-- @param string
util.shell = os.getenv("SHELL") or "/bin/sh"
-- @tfield string awful.util.shell
--- Execute a system command and road the output.
-- This function implementation **has been removed** and no longer
@ -484,6 +483,19 @@ function util.round(x)
return gmath.round(x)
end
return util
return setmetatable(util, {
__index = function(_, key)
if key == "shell" then
return watcher._shell
end
end,
__newindex = function(_, key, value)
if key == "shell" then
watcher._shell = value
else
rawset(util, key, value)
end
end
})
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,859 +0,0 @@
---------------------------------------------------------------------------
--- Allows to use the wibox widget system to draw the wallpaper.
--
-- Rather than simply having a function to set an image
-- (stretched, centered or tiled) like most wallpaper tools, this module
-- leverage the full widget system to draw the wallpaper. Note that the result
-- is **not** interactive. If you want an interactive wallpaper, better use
-- a `wibox` object with the `below` property set to `true` and maximized
-- using `awful.placement.maximized`.
--
-- It is possible to create an `awful.wallpaper` object from any places, but
-- it is recommanded to do it from the `request::wallpaper` signal handler.
-- That signal is called everytime something which could affect the wallpaper
-- rendering changes, such as new screens.
--
-- Single image
-- ============
--
-- This is the default `rc.lua` wallpaper format. It fills the whole screen
-- and stretches the image while keeping the aspect ratio.
--
--@DOC_awful_wallpaper_mazimized1_EXAMPLE@
--
-- If the image aspect ratio doesn't match, the `bg` property can be used to
-- fill the empty area:
--
--@DOC_awful_wallpaper_mazimized2_EXAMPLE@
--
-- It is also possible to stretch the image:
--
--@DOC_awful_wallpaper_mazimized3_EXAMPLE@
--
-- Finally, it is also possible to use simpler "branding" in a corner using
-- `awful.placement`:
--
--@DOC_awful_wallpaper_corner1_EXAMPLE@
--
-- Tiled
-- =====
--
-- This example tiles an image:
--
--@DOC_awful_wallpaper_tiled1_EXAMPLE@
--
-- This one tiles a shape using the `wibox.widget.separator` widget:
--
--@DOC_awful_wallpaper_tiled2_EXAMPLE@
--
-- See the `wibox.container.tile` for more advanced tiling configuration
-- options.
--
-- Solid colors and gradients
-- ==========================
--
-- Solid colors can be set using the `bg` property mentionned above. It
-- is also possible to set gradients:
--
--@DOC_awful_wallpaper_gradient1_EXAMPLE@
--
--@DOC_awful_wallpaper_gradient2_EXAMPLE@
--
-- Widgets
-- =======
--
-- It is possible to create a wallpaper using any widgets. However, keep
-- in mind that the wallpaper surface is not interactive, so some widgets
-- like the sliders will render, but will not behave correctly. Also, it
-- is not recommanded to update the wallpaper too often. This is very slow.
--
--@DOC_awful_wallpaper_widget2_EXAMPLE@
--
-- Cairo graphics API
-- ==================
--
-- AwesomeWM widgets are backed by Cairo. So it is always possible to get
-- access to the Cairo context directly to do some vector art:
--
--@DOC_awful_wallpaper_widget1_EXAMPLE@
--
--
-- SVG vector images
-- =================
--
-- SVG are supported if `librsvg` is installed. Please note that `librsvg`
-- doesn't implement all filters you might find in the latest version of
-- your web browser. It is possible some advanced SVG will not look exactly
-- as they do in a web browser or even Inkscape. However, for most images,
-- it should look identical.
--
-- Our SVG support goes beyond simple rendering. It is possible to set a
-- custom CSS stylesheet (see `wibox.widget.imagebox.stylesheet`):
--
--@DOC_awful_wallpaper_svg_EXAMPLE@
--
-- Note that in the example above, it is raw SVG code, but it is also possible
-- to use a file path. If you have a `.svgz`, you need to uncompress it first
-- using `gunzip` or a software like Inkscape.
--
-- Multiple screen
-- ===============
--
-- The default `rc.lua` creates a new wallpaper everytime `request::wallpaper`
-- is emitted. This is well suited for having a single wallpaper per screen.
-- It is also much simpler to implement slideshows and add/remove screens.
--
-- However, it isn't wall suited for wallpaper rendered across multiple screens.
-- For this case, it is better to capture the return value of `awful.wallpaper {}`
-- as a global variable. Then manually call `add_screen` and `remove_screen` when
-- needed. A shortcut can be to do:
--
-- @DOC_text_awful_wallpaper_multi_screen_EXAMPLE@
--
-- Slideshow
-- =========
--
-- Slideshows (changing the wallpaper after a few minutes) can be implemented
-- directly using a timer and callback, but it is more elegant to simply request
-- a new wallpaper, then get a random image from within the request handler. This
-- way, corner cases such as adding and removing screens are handled:
--
--@DOC_awful_wallpaper_slideshow1_EXAMPLE@
--
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2019 Emmanuel Lepage Vallee
-- @popupmod awful.wallpaper
---------------------------------------------------------------------------
require("awful._compat")
local gtable = require( "gears.table" )
local gobject = require( "gears.object" )
local gcolor = require( "gears.color" )
local gtimer = require( "gears.timer" )
local surface = require( "gears.surface" )
local base = require( "wibox.widget.base" )
local background = require( "wibox.container.background")
local beautiful = require( "beautiful" )
local cairo = require( "lgi" ).cairo
local draw = require( "wibox.widget" ).draw_to_cairo_context
local grect = require( "gears.geometry" ).rectangle
local capi = { screen = screen, root = root }
local module = {}
local function get_screen(s)
return s and capi.screen[s]
end
-- Screen as key, wallpaper as values.
local pending_repaint = setmetatable({}, {__mode = 'k'})
local backgrounds = setmetatable({}, {__mode = 'k'})
local panning_modes = {}
-- Get a list of all screen areas.
local function get_rectangles(screens, honor_workarea, honor_padding)
local ret = {}
for _, s in ipairs(screens) do
table.insert(ret, s:get_bounding_geometry {
honor_padding = honor_padding,
honor_workarea = honor_workarea
})
end
return ret
end
-- Outer perimeter of all rectangles.
function panning_modes.outer(self)
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
local p1, p2 = {x = math.huge, y = math.huge}, {x = 0, y = 0}
for _, rect in ipairs(rectangles) do
p1.x, p1.y = math.min(p1.x, rect.x), math.min(p1.y, rect.y)
p2.x, p2.y = math.max(p2.x, rect.x + rect.width), math.max(p2.y, rect.y + rect.height)
end
-- Never try to paint this, it would freeze the system.
assert(p1.x ~= math.huge and p1.y ~= math.huge, "Setting wallpaper failed"..#self.screens)
return {
x = p1.x,
y = p1.y,
width = p2.x - p1.x,
height = p2.y - p1.y,
}
end
-- Horizontal inner perimeter of all rectangles.
function panning_modes.inner_horizontal(self)
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
local p1, p2 = {x = math.huge, y = 0}, {x = 0, y = math.huge}
for _, rect in ipairs(rectangles) do
p1.x, p1.y = math.min(p1.x, rect.x), math.max(p1.y, rect.y)
p2.x, p2.y = math.max(p2.x, rect.x + rect.width), math.min(p2.y, rect.y + rect.height)
end
-- Never try to paint this, it would freeze the system.
assert(p1.x ~= math.huge and p2.y ~= math.huge, "Setting wallpaper failed")
return {
x = p1.x,
y = p1.y,
width = p2.x - p1.x,
height = p2.y - p1.y,
}
end
-- Vertical inner perimeter of all rectangles.
function panning_modes.inner_vertical(self)
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
local p1, p2 = {x = 0, y = math.huge}, {x = math.huge, y = 0}
for _, rect in ipairs(rectangles) do
p1.x, p1.y = math.max(p1.x, rect.x), math.min(p1.y, rect.y)
p2.x, p2.y = math.min(p2.x, rect.x + rect.width), math.max(p2.y, rect.y + rect.height)
end
-- Never try to paint this, it would freeze the system.
assert(p1.y ~= math.huge and p2.a ~= math.huge, "Setting wallpaper failed")
return {
x = p1.x,
y = p1.y,
width = p2.x - p1.x,
height = p2.y - p1.y,
}
end
-- Best or vertical and horizontal "inner" modes.
function panning_modes.inner(self)
local vert = panning_modes.inner_vertical(self)
local hori = panning_modes.inner_horizontal(self)
if vert.width <= 0 or vert.height <= 0 then return hori end
if hori.width <= 0 or hori.height <= 0 then return vert end
if vert.width * vert.height > hori.width * hori.height then
return vert
else
return hori
end
end
local function paint()
if not next(pending_repaint) then return end
local root_width, root_height = capi.root.size()
-- Get the current wallpaper content.
local source = surface(root.wallpaper())
local target, cr
-- It's possible that a wallpaper for 1 screen is set using another tool, so make
-- sure we copy the current content.
if source then
target = source:create_similar(cairo.Content.COLOR, root_width, root_height)
cr = cairo.Context(target)
-- Copy the old wallpaper to the new one
cr:save()
cr.operator = cairo.Operator.SOURCE
cr:set_source_surface(source, 0, 0)
for s in screen do
cr:rectangle(
s.geometry.x,
s.geometry.y,
s.geometry.width,
s.geometry.height
)
end
cr:clip()
cr:paint()
cr:restore()
else
target = cairo.ImageSurface(cairo.Format.RGB32, root_width, root_height)
cr = cairo.Context(target)
end
local walls = {}
for _, wall in pairs(backgrounds) do
walls[wall] = true
end
-- Not supposed to happen, but there is enough API surface for
-- it to be a side effect of some signals. Calling the panning
-- mode callback with zero screen is not supported.
if not next(walls) then
return
end
for wall in pairs(walls) do
local geo = type(wall._private.panning_area) == "function" and
wall._private.panning_area(wall) or
panning_modes[wall._private.panning_area](wall)
-- If false, this panning area isn't well suited for the screen geometry.
if geo.width > 0 or geo.height > 0 then
local uncovered_areas = grect.area_remove(get_rectangles(wall.screens, false, false), geo)
cr:save()
-- Prevent overwrite then there is multiple non-continuous screens.
for _, s in ipairs(wall.screens) do
cr:rectangle(
s.geometry.x,
s.geometry.y,
s.geometry.width,
s.geometry.height
)
end
cr:clip()
-- The older surface might contain garbage, optionally clean it.
if wall.uncovered_areas_color then
cr:set_source(gcolor(wall.uncovered_areas_color))
for _, area in ipairs(uncovered_areas) do
cr:rectangle(area.x, area.y, area.width, area.height)
cr:fill()
end
end
if not wall._private.container then
wall._private.container = background()
wall._private.container.bg = wall._private.bg or beautiful.wallpaper_bg or "#000000"
wall._private.container.fg = wall._private.fg or beautiful.wallpaper_fg or "#ffffff"
wall._private.container.widget = wall.widget
end
local a_context = {
dpi = wall._private.context.dpi
}
-- Pick the lowest DPI.
if not a_context.dpi then
a_context.dpi = math.huge
for _, s in ipairs(wall.screens) do
a_context.dpi = math.min(
s.dpi and s.dpi or s.preferred_dpi, a_context.dpi
)
end
end
-- Fallback.
if not a_context.dpi then
a_context.dpi = 96
end
cr:translate(geo.x, geo.y)
draw(wall._private.container, cr, geo.width, geo.height, a_context)
cr:restore()
end
end
-- Set the wallpaper.
local pattern = cairo.Pattern.create_for_surface(target)
capi.root.wallpaper(pattern)
-- Limit some potential GC induced increase in memory usage.
-- But really, is someone is trying to apply wallpaper changes more
-- often than the GC is executed, they are doing it wrong.
target:finish()
end
local mutex = false
-- Uploading the surface to X11 is *very* resource intensive. Given the updates
-- will often happen in batch (like startup), make sure to only do one "real"
-- update.
local function update()
if mutex then return end
mutex = true
gtimer.delayed_call(function()
-- Remove the mutex first in case `paint()` raises an exception.
mutex = false
paint()
end)
end
capi.screen.connect_signal("removed", function(s)
if not backgrounds[s] then return end
backgrounds[s]:remove_screen(s)
update()
end)
capi.screen.connect_signal("property::geometry", function(s)
if not backgrounds[s] then return end
backgrounds[s]:repaint()
end)
--- The wallpaper widget.
--
-- When set, instead of using the `image_path` or `surface` properties, the
-- wallpaper will be defined as a normal `wibox` widget tree.
--
-- @property widget
-- @tparam wibox.widget widget
-- @see wibox.widget.imagebox
-- @see wibox.container.tile
--- The wallpaper DPI (dots per inch).
--
-- Each screen has a DPI. This value will be used by default, but sometime it
-- is useful to override the screen DPI and use a custom one. This makes
-- possible, for example, to draw the widgets bigger than they would otherwise
-- be.
--
-- If not DPI is defined, it will use the smallest DPI from any of the screen.
--
-- In this example, there is 3 screens with DPI of 100, 200 and 300. As you can
-- see, only the text size is affected. Many widgetds are DPI aware, but not all
-- of them. This is either because DPI isn't relevant to them or simply because it
-- isn't supported (like `wibox.widget.graph`).
--
-- @DOC_awful_wallpaper_dpi1_EXAMPLE@
--
-- @property dpi
-- @tparam[opt=screen.dpi] number dpi
-- @see screen
-- @see screen.dpi
--- The wallpaper screen.
--
-- Note that there can only be one wallpaper per screen. If there is more, one
-- will be chosen and all other ignored.
--
-- @property screen
-- @tparam screen screen
-- @see screens
-- @see add_screen
-- @see remove_screen
--- A list of screen for this wallpaper.
--
--@DOC_awful_wallpaper_screens1_EXAMPLE@
--
-- Some large wallpaper are made to span multiple screens.
-- @property screens
-- @tparam table screens
-- @see screen
-- @see add_screen
-- @see remove_screen
-- @see detach
--- The background color.
--
-- It will be used as the "fill" color if the `image` doesn't take all the
-- screen space. It will also be the default background for the `widget.
--
-- As usual with colors in `AwesomeWM`, it can also be a gradient or a pattern.
--
-- @property bg
-- @tparam gears.color bg
-- @see gears.color
--- The foreground color.
--
-- This will be used by the `widget` (if any).
--
-- As usual with colors in `AwesomeWM`, it can also be a gradient or a pattern.
--
-- @property fg
-- @tparam gears.color fg
-- @see gears.color
--- The default wallpaper background color.
-- @beautiful beautiful.wallpaper_bg
-- @tparam gears.color wallpaper_bg
-- @see bg
--- The default wallpaper foreground color.
--
-- This is useful when using widgets or text in the wallpaper. A wallpaper
-- created from a single image wont use this.
--
-- @beautiful beautiful.wallpaper_fg
-- @tparam gears.color wallpaper_fg
-- @see bg
--- Honor the workarea.
--
-- When set to `true`, the wallpaper will only fill the workarea space instead
-- of the entire screen. This means it wont be drawn below the `awful.wibar` or
-- docked clients. This is useful when using opaque bars. Note that it can cause
-- aspect ratio issues for the wallpaper `image` and add bars colored with the
-- `bg` color on the sides.
--
--@DOC_awful_wallpaper_workarea1_EXAMPLE@
--
-- @property honor_workarea
-- @tparam[opt=false] boolean honor_workarea
-- @see honor_padding
-- @see uncovered_areas
--- Honor the screen padding.
--
-- When set, this will look at the `screen.padding` property to restrict the
-- area where the wallpaper is rendered.
--
-- @DOC_awful_wallpaper_padding1_EXAMPLE@
--
-- @property honor_padding
-- @tparam boolean honor_padding
-- @see honor_workarea
-- @see uncovered_areas
--- Returns the list of screen(s) area which won't be covered by the wallpaper.
--
-- When `honor_workarea`, `honor_padding` or panning are used, some section of
-- the screen won't have a wallpaper. This returns a list of areas tables. Each
-- table has a `x`, `y`, `width` and `height` key.
--
-- @property uncovered_areas
-- @tparam table uncovered_areas
-- @see honor_workarea
-- @see honor_padding
-- @see uncovered_areas_color
--- The color for the uncovered areas.
--
-- Some application rely on the wallpaper for "fake" transparency. Even if an
-- area is hidden under a wibar (or other clients), its background can still
-- become visible. If you use such application and change your screen geometry
-- often enough, it is possible some areas would become filled with the remains
-- of previous wallpapers. This property allows to clean those areas with a solid
-- color or a gradient.
--
-- @property uncovered_areas_color
-- @tparam gears.color uncovered_areas_color
-- @see uncovered_areas
--- Defines where the wallpaper is placed when there is multiple screens.
--
-- When there is more than 1 screen, it is possible they don't have the same
-- resolution, position or orientation. Panning the wallpaper over them may look
-- better if a continuous rectangle is used rather than creating a virtual rectangle
-- around all screens.
--
-- The default algorithms are:
--
-- **outer:** *(default)*
--
-- Draw an imaginary rectangle around all screens.
--
-- @DOC_awful_wallpaper_panning_outer_EXAMPLE@
--
-- **inner:**
--
-- Take the largest area or either `inner_horizontal` or `inner_vertical`.
--
-- @DOC_awful_wallpaper_panning_inner_EXAMPLE@
--
-- **inner_horizontal:**
--
-- Take the smallest `x` value, the largest `x+width`, the smallest `y`
-- and the smallest `y+height`.
--
-- @DOC_awful_wallpaper_panning_inner_horizontal_EXAMPLE@
--
-- **inner_vertical:**
--
-- Take the smallest `y` value, the largest `y+height`, the smallest `x`
-- and the smallest `x+width`.
--
-- @DOC_awful_wallpaper_panning_inner_vertical_EXAMPLE@
--
-- **Custom function:**
--
-- It is also possible to define a custom function.
--
-- @DOC_awful_wallpaper_panning_custom_EXAMPLE@
--
-- @property panning_area
-- @tparam function|string panning_area
-- @see uncovered_areas
function module:set_panning_area(value)
value = value or "outer"
assert(type(value) == "function" or panning_modes[value], "Invalid panning mode: "..tostring(value))
self._private.panning_area = value
self:repaint()
self:emit_signal("property::panning_area", value)
end
function module:set_widget(w)
self._private.widget = base.make_widget_from_value(w)
if self._private.container then
self._private.container.widget = self._private.widget
end
self:repaint()
end
function module:get_widget()
return self._private.widget
end
function module:set_dpi(dpi)
self._private.context.dpi = dpi
self:repaint()
end
function module:get_dpi()
return self._private.context.dpi
end
function module:set_screen(s)
if not s then return end
self:_clear()
self:add_screen(s)
end
for _, prop in ipairs {"bg", "fg"} do
module["set_"..prop] = function(self, color)
if self._private.container then
self._private.container[prop] = color
end
self._private[prop] = color
self:repaint()
end
end
function module:get_uncovered_areas()
local geo = type(self._private.panning_area) == "function" and
self._private.panning_area(self) or
panning_modes[self._private.panning_area](self)
return grect.area_remove(get_rectangles(self.screens, false, false), geo)
end
function module:set_screens(screens)
local to_rem = {}
-- All screens.
-- The copy is needed because it's a metatable, `ipairs` doesn't work
-- correctly in all Lua versions.
if screens == capi.screen then
screens = {}
for s in capi.screen do
table.insert(screens, s)
end
end
for _, s in ipairs(screens) do
to_rem[get_screen(s)] = true
end
for _, s in ipairs(self.screens) do
to_rem[get_screen(s)] = nil
end
for _, s in ipairs(screens) do
s = get_screen(s)
self:add_screen(s)
to_rem[s] = nil
end
for s, remove in pairs(to_rem) do
if remove then
self:remove_screen(s)
end
end
end
function module:get_screens()
return self._private.screens
end
--- Add another screen (enable panning).
--
-- **Before:**
--
--@DOC_awful_wallpaper_add_screen1_EXAMPLE@
--
-- **After:**
--
--@DOC_awful_wallpaper_add_screen2_EXAMPLE@
--
-- Also note that adding a non-continuous screen might not work well,
-- but will not automatically add the screens in between:
--
--@DOC_awful_wallpaper_add_screen3_EXAMPLE@
--
-- @method add_screen
-- @tparam screen screen The screen object.
-- @see remove_screen
function module:add_screen(s)
s = get_screen(s)
for _, s2 in ipairs(self._private.screens) do
if s == s2 then return end
end
table.insert(self._private.screens, s)
if backgrounds[s] and backgrounds[s] ~= self then
backgrounds[s]:remove_screen(s)
end
backgrounds[s] = self
self:repaint()
end
--- Detach the wallpaper from all screens.
--
-- Adding a new wallpaper to a screen will automatically
-- detach the older one. However there is some case when
-- it is useful to call this manually. For example, when
-- adding a new panned wallpaper, it is possible that 2
-- wallpaper will have an overlap.
--
-- @method detach
-- @see remove_screen
-- @see add_screen
function module:detach()
local screens = gtable.clone(self.screens)
for _, s in ipairs(screens) do
self:remove_screen(s)
end
end
function module:_clear()
self._private.screens = setmetatable({}, {__mode = "v"})
update()
end
--- Repaint the wallpaper.
--
-- By default, even if the widget changes, the wallpaper will **NOT** be
-- automatically repainted. Repainting the native X11 wallpaper is slow and
-- it would be too easy to accidentally cause a performance problem. If you
-- really need to repaint the wallpaper, call this method.
--
-- @method repaint
function module:repaint()
for _, s in ipairs(self._private.screens) do
pending_repaint[s] = true
end
update()
end
--- Remove a screen.
--
-- Calling this will remove a screen, but will **not** repaint its area.
-- In this example, the wallpaper was spanning all 3 screens and the
-- first screen was removed:
--
-- @DOC_awful_wallpaper_remove_screen1_EXAMPLE@
--
-- As you can see, the content of screen 1 still looks like it is part of
-- the 3 screen wallpaper. The only use case for calling this method is if
-- you use a 3rd party tools to change the wallpaper.
--
-- If you wish to simply remove a screen and not have leftover content, it is
-- simpler to just create a new wallpaper for that screen:
--
-- @DOC_awful_wallpaper_remove_screen2_EXAMPLE@
--
-- @method remove_screen
-- @tparam screen screen The screen to remove.
-- @see detach
-- @see add_screen
-- @see screens
function module:remove_screen(s)
s = get_screen(s)
for k, s2 in ipairs(self._private.screens) do
if s == s2 then
table.remove(self._private.screens, k)
end
end
backgrounds[s] = nil
self:repaint()
end
--- Create a wallpaper.
--
-- Note that all parameters are not required. Please refer to the module description and examples to understand parameters usages.
--
-- @constructorfct awful.wallpaper
-- @tparam table args
-- @tparam[opt] wibox.widget args.widget The wallpaper widget.
-- @tparam[opt] number args.dpi The wallpaper DPI (dots per inch).
-- @tparam[opt] screen args.screen The wallpaper screen.
-- @tparam[opt] table args.screens A list of screen for this wallpaper. Use this parameter as a remplacement for `args.screen` to manage multiscreen wallpaper. (Note: the expected table should be an array-like table `{screen1, screen2, ...}`)
-- @tparam[opt] gears.color args.bg The background color.
-- @tparam[opt] gears.color args.fg The foreground color.
-- @tparam[opt] gears.color args.uncovered_areas_color The color for the uncovered areas.
-- @tparam[opt] boolean args.honor_workarea Honor the workarea.
-- @tparam[opt] boolean args.honor_padding Honor the screen padding.
-- @tparam[opt] table args.uncovered_areas Returns the list of screen(s) area which won't be covered by the wallpaper.
-- @tparam[opt] function|string args.panning_area Defines where the wallpaper is placed when there is multiple screens.
local function new(_, args)
args = args or {}
local ret = gobject {
enable_auto_signals = true,
enable_properties = true,
}
rawset(ret, "_private", {})
ret._private.context = {}
ret._private.panning_area = "outer"
gtable.crush(ret, module, true)
ret:_clear()
-- Set the screen or screens first to avoid a race condition
-- with the other setters.
local args_screen, args_screens = args.screen, args.screens
if args_screen then
ret.screen = args_screen
elseif args_screens then
ret.screens = args_screens
end
-- Avoid crushing `screen` and `screens` twice.
args.screen, args.screens = nil, nil
gtable.crush(ret, args, false)
args.screen, args.screens = args_screen, args_screens
return ret
end
return setmetatable(module, {__call = new})

View File

@ -1,19 +1,16 @@
---------------------------------------------------------------------------
--- The main AwesomeWM "bar" module.
--
--- Wibox module for awful.
-- This module allows you to easily create wibox and attach them to the edge of
-- a screen.
--
--@DOC_awful_wibar_default_EXAMPLE@
--
-- You can even have vertical bars too.
--
--@DOC_awful_wibar_left_EXAMPLE@
--
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2016 Emmanuel Lepage Vallee
-- @popupmod awful.wibar
-- @supermodule awful.popup
---------------------------------------------------------------------------
-- Grab environment we need
@ -30,7 +27,6 @@ local wibox = require("wibox")
local beautiful = require("beautiful")
local gdebug = require("gears.debug")
local placement = require("awful.placement")
local gtable = require("gears.table")
local function get_screen(s)
return s and capi.screen[s]
@ -42,94 +38,16 @@ local awfulwibar = { mt = {} }
-- It's an array so it is ordered.
local wiboxes = setmetatable({}, {__mode = "v"})
local opposite_margin = {
top = "bottom",
bottom = "top",
left = "right",
right = "left"
}
local align_map = {
top = "left",
left = "top",
bottom = "right",
right = "bottom",
centered = "centered"
}
--- If the wibar needs to be stretched to fill the screen.
--
-- @DOC_awful_wibar_stretch_EXAMPLE@
--
-- @property stretch
-- @tparam boolean stretch
-- @propbeautiful
-- @propemits true false
-- @see align
--- How to align non-stretched wibars.
--
-- Values are:
--
-- * `"top"`
-- * `"bottom"`
-- * `"left"`
-- * `"right"`
-- * `"centered"`
--
-- @DOC_awful_wibar_align_EXAMPLE@
--
-- @property align
-- @tparam string align
-- @propbeautiful
-- @propemits true false
-- @see stretch
--- Margins on each side of the wibar.
--
-- It can either be a table with `top`, `bottom`, `left` and `right`
-- properties, or a single number that applies to all four sides.
--
-- @DOC_awful_wibar_margins_EXAMPLE@
--
-- @property margins
-- @tparam number|table margins
-- @propbeautiful
-- @propemits true false
--- If the wibar needs to be stretched to fill the screen.
--
-- @beautiful beautiful.wibar_stretch
-- @tparam boolean stretch
--- Allow or deny the tiled clients to cover the wibar.
--
-- In the example below, we can see that the first screen workarea
-- shrunk by the height of the wibar while the second screen is
-- unchanged.
--
-- @DOC_screen_wibar_workarea_EXAMPLE@
--
-- @property restrict_workarea
-- @tparam[opt=true] boolean restrict_workarea
-- @propemits true false
-- @see client.struts
-- @see screen.workarea
--- If there is both vertical and horizontal wibar, give more space to vertical ones.
--
-- By default, if multiple wibars risk overlapping, it will be resolved
-- by giving more space to the horizontal one:
--
-- ![wibar position](../images/AUTOGEN_awful_wibar_position.svg)
--
-- If this variable is to to `true`, it will behave like:
--
-- @DOC_awful_wibar_position2_EXAMPLE@
--
-- @beautiful beautiful.wibar_favor_vertical
-- @tparam[opt=false] boolean favor_vertical
--- The wibar border width.
-- @beautiful beautiful.wibar_border_width
-- @tparam integer border_width
@ -178,15 +96,6 @@ local align_map = {
-- @beautiful beautiful.wibar_shape
-- @tparam gears.shape shape
--- The wibar's margins.
-- @beautiful beautiful.wibar_margins
-- @tparam number|table margins
--- The wibar's alignments.
-- @beautiful beautiful.wibar_align
-- @tparam string align
-- Compute the margin on one side
local function get_margin(w, position, auto_stop)
local h_or_w = (position == "top" or position == "bottom") and "height" or "width"
@ -198,15 +107,7 @@ local function get_margin(w, position, auto_stop)
if v.position == position and v.screen == w.screen and v.visible then
ret = ret + v[h_or_w]
local wb_margins = v.margins
if wb_margins then
ret = ret + wb_margins[position] + wb_margins[opposite_margin[position]]
end
end
end
return ret
@ -218,49 +119,32 @@ local function get_margins(w)
local position = w.position
assert(position)
local margins = gtable.clone(w._private.margins)
local margins = {left=0, right=0, top=0, bottom=0}
margins[position] = margins[position] + get_margin(w, position, true)
margins[position] = get_margin(w, position, true)
-- Avoid overlapping wibars
if (position == "left" or position == "right") and not beautiful.wibar_favor_vertical then
if position == "left" or position == "right" then
margins.top = get_margin(w, "top" )
margins.bottom = get_margin(w, "bottom")
elseif (position == "top" or position == "bottom") and beautiful.wibar_favor_vertical then
margins.left = get_margin(w, "left" )
margins.right = get_margin(w, "right")
end
return margins
end
-- Create the placement function
local function gen_placement(position, align, stretch)
local function gen_placement(position, stretch)
local maximize = (position == "right" or position == "left") and
"maximize_vertically" or "maximize_horizontally"
local corner = nil
if align ~= "centered" then
if position == "right" or position == "left" then
corner = placement[align .. "_" .. position]
or placement[align_map[align] .. "_" .. position]
else
corner = placement[position .. "_" .. align]
or placement[position .. "_" .. align_map[align]]
end
end
corner = corner or placement[position]
return corner + (stretch and placement[maximize] or nil)
return placement[position] + (stretch and placement[maximize] or nil)
end
-- Attach the placement function.
local function attach(wb, position)
gen_placement(position, wb._private.align, wb._stretch)(wb, {
local function attach(wb, align)
gen_placement(align, wb._stretch)(wb, {
attach = true,
update_workarea = wb._private.restrict_workarea,
update_workarea = true,
margins = get_margins(wb)
})
end
@ -288,23 +172,15 @@ end
-- * top
-- * bottom
--
-- @DOC_awful_wibar_position_EXAMPLE@
--
-- @property position
-- @tparam string position Either "left", right", "top" or "bottom"
-- @propemits true false
function awfulwibar.get_position(wb)
local function get_position(wb)
return wb._position or "top"
end
function awfulwibar.set_position(wb, position, screen)
if position == wb._position then return end
if screen then
gdebug.deprecate("Use `wb.screen = screen` instead of awful.wibar.set_position", {deprecated_in=4})
end
local function set_position(wb, position, skip_reattach)
-- Detach first to avoid any uneeded callbacks
if wb.detach_callback then
wb.detach_callback()
@ -315,12 +191,14 @@ function awfulwibar.set_position(wb, position, screen)
-- Move the wibar to the end of the list to avoid messing up the others in
-- case there is stacked wibars on one side.
if wb._position then
for k, w in ipairs(wiboxes) do
if w == wb then
table.remove(wiboxes, k)
end
end
table.insert(wiboxes, wb)
end
-- In case the position changed, it may be necessary to reset the size
if (wb._position == "left" or wb._position == "right")
@ -339,7 +217,7 @@ function awfulwibar.set_position(wb, position, screen)
-- A way to skip reattach is required when first adding a wibar as it's not
-- in the `wiboxes` table yet and can't be added until it's attached.
if not wb._private.skip_reattach then
if not skip_reattach then
-- Changing the position will also cause the other margins to be invalidated.
-- For example, adding a wibar to the top will change the margins of any left
-- or right wibars. To solve, this, they need to be re-attached.
@ -349,11 +227,11 @@ function awfulwibar.set_position(wb, position, screen)
wb:emit_signal("property::position", position)
end
function awfulwibar.get_stretch(w)
local function get_stretch(w)
return w._stretch
end
function awfulwibar.set_stretch(w, value)
local function set_stretch(w, value)
w._stretch = value
attach(w, w.position)
@ -361,89 +239,10 @@ function awfulwibar.set_stretch(w, value)
w:emit_signal("property::stretch", value)
end
function awfulwibar.get_restrict_workarea(w)
return w._private.restrict_workarea
end
function awfulwibar.set_restrict_workarea(w, value)
w._private.restrict_workarea = value
attach(w, w.position)
w:emit_signal("property::restrict_workarea", value)
end
function awfulwibar.set_margins(w, value)
if type(value) == "number" then
value = {
top = value,
bottom = value,
right = value,
left = value,
}
end
local old = gtable.crush({
left = 0,
right = 0,
top = 0,
bottom = 0
}, w._private.margins or {}, true)
value = gtable.crush(old, value or {}, true)
w._private.margins = value
attach(w, w.position)
w:emit_signal("property::margins", value)
end
-- Allow each margins to be set individually.
local function meta_margins(self)
return setmetatable({}, {
__index = self._private.margins,
__newindex = function(_, k, v)
self._private.margins[k] = v
awfulwibar.set_margins(self, self._private.margins)
end
})
end
function awfulwibar.get_margins(self)
return self._private.meta_margins
end
function awfulwibar.get_align(self)
return self._private.align
end
function awfulwibar.set_align(self, value, screen)
if value == "center" then
gdebug.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'", {deprecated_in=4})
value = "centered"
end
if screen then
gdebug.deprecate("awful.wibar.align 'screen' argument is deprecated", {deprecated_in=4})
end
assert(align_map[value])
self._private.align = value
attach(self, self.position)
self:emit_signal("property::align", value)
end
--- Remove a wibar.
-- @method remove
function awfulwibar.remove(self)
local function remove(self)
self.visible = false
if self.detach_callback then
@ -460,6 +259,26 @@ function awfulwibar.remove(self)
self._screen = nil
end
--- Get a wibox position if it has been set, or return top.
-- @param wb The wibox
-- @deprecated awful.wibar.get_position
-- @return The wibox position.
function awfulwibar.get_position(wb)
gdebug.deprecate("Use wb:get_position() instead of awful.wibar.get_position", {deprecated_in=4})
return get_position(wb)
end
--- Put a wibox on a screen at this position.
-- @param wb The wibox to attach.
-- @param position The position: top, bottom left or right.
-- @param screen This argument is deprecated, use wb.screen directly.
-- @deprecated awful.wibar.set_position
function awfulwibar.set_position(wb, position, screen) --luacheck: no unused args
gdebug.deprecate("Use wb:set_position(position) instead of awful.wibar.set_position", {deprecated_in=4})
set_position(wb, position)
end
--- Attach a wibox to a screen.
--
-- This function has been moved to the `awful.placement` module. Calling this
@ -470,7 +289,7 @@ end
-- @param screen The screen to attach to
-- @see awful.placement
-- @deprecated awful.wibar.attach
local function legacy_attach(wb, position, screen) --luacheck: no unused args
function awfulwibar.attach(wb, position, screen) --luacheck: no unused args
gdebug.deprecate("awful.wibar.attach is deprecated, use the 'attach' property"..
" of awful.placement. This method doesn't do anything anymore",
{deprecated_in=4}
@ -499,7 +318,7 @@ end
-- directly.
-- @deprecated awful.wibar.align
-- @see awful.placement.align
local function legacy_align(wb, align, screen) --luacheck: no unused args
function awfulwibar.align(wb, align, screen) --luacheck: no unused args
if align == "center" then
gdebug.deprecate("awful.wibar.align(wb, 'center' is deprecated, use 'centered'", {deprecated_in=4})
align = "centered"
@ -531,9 +350,6 @@ end
-- @tparam[opt=nil] table args
-- @tparam string args.position The position.
-- @tparam string args.stretch If the wibar need to be stretched to fill the screen.
-- @tparam boolean args.restrict_workarea Allow or deny the tiled clients to cover the wibar.
-- @tparam string args.align How to align non-stretched wibars.
-- @tparam table|number args.margins The wibar margins.
--@DOC_wibox_constructor_COMMON@
-- @return The new wibar
-- @constructorfct awful.wibar
@ -583,7 +399,7 @@ function awfulwibar.new(args)
-- The C code scans the table directly, so metatable magic cannot be used.
for _, prop in ipairs {
"border_width", "border_color", "font", "opacity", "ontop", "cursor",
"bgimage", "bg", "fg", "type", "stretch", "shape", "margins", "align"
"bgimage", "bg", "fg", "type", "stretch", "shape"
} do
if (args[prop] == nil) and beautiful["wibar_"..prop] ~= nil then
args[prop] = beautiful["wibar_"..prop]
@ -592,37 +408,25 @@ function awfulwibar.new(args)
local w = wibox(args)
w._private.align = (args.align and align_map[args.align]) and args.align or "centered"
w._private.margins = {
left = 0,
right = 0,
top = 0,
bottom = 0
}
w._private.meta_margins = meta_margins(w)
w._private.restrict_workarea = true
-- `w` needs to be inserted in `wiboxes` before reattach or its own offset
-- will not be taken into account by the "older" wibars when `reattach` is
-- called. `skip_reattach` is required.
w._private.skip_reattach = true
w.screen = screen
w._screen = screen --HACK When a screen is removed, then getbycoords wont work
w._stretch = args.stretch == nil and has_to_stretch or args.stretch
w.get_position = get_position
w.set_position = set_position
w.get_stretch = get_stretch
w.set_stretch = set_stretch
w.remove = remove
if args.visible == nil then w.visible = true end
gtable.crush(w, awfulwibar, true)
gtable.crush(w, args, false)
-- `w` needs to be inserted in `wiboxes` before reattach or its own offset
-- will not be taken into account by the "older" wibars when `reattach` is
-- called. `skip_reattach` is required.
w:set_position(position, true)
-- Now, let set_position behave normally.
w._private.skip_reattach = false
awfulwibar.set_margins(w, args.margins)
table.insert(wiboxes, w)
-- Force all the wibars to be moved
reattach(w)
@ -650,14 +454,6 @@ function awfulwibar.mt:__call(...)
return awfulwibar.new(...)
end
function awfulwibar.mt:__index(_, k)
if k == "align" then
return legacy_align
elseif k == "attach" then
return legacy_attach
end
end
--@DOC_wibox_COMMON@
--@DOC_object_COMMON@

View File

@ -6,7 +6,6 @@
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @widgetmod awful.widget.button
-- @supermodule wibox.widget.imagebox
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -66,6 +65,10 @@ function button.mt:__call(...)
return button.new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(button, button.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,10 +1,7 @@
---------------------------------------------------------------------------
--- Container showing the icon of a client.
-- @author Uli Schlachter
-- @copyright 2017 Uli Schlachter
-- @widgetmod awful.widget.clienticon
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local base = require("wibox.widget.base")
local surface = require("gears.surface")
@ -126,6 +123,10 @@ client.connect_signal("property::icon", function(c)
end
end)
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(clienticon, {
__call = function(_, ...)
return new(...)

View File

@ -11,7 +11,6 @@
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @widgetmod awful.widget.launcher
-- @supermodule awful.widget.button
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -51,6 +50,10 @@ function launcher.mt:__call(...)
return launcher.new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(launcher, launcher.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -6,7 +6,6 @@
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @widgetmod awful.widget.layoutbox
-- @supermodule wibox.layout.fixed
---------------------------------------------------------------------------
local setmetatable = setmetatable

View File

@ -21,7 +21,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2010, 2018 Emmanuel Lepage Vallee
-- @widgetmod awful.widget.layoutlist
-- @supermodule wibox.widget.base
----------------------------------------------------------------------------
local capi = {screen = screen, tag = tag}
@ -445,4 +444,8 @@ local function new(_, args)
return ret
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(module, {__call = new})

View File

@ -5,7 +5,6 @@
-- @author Uli Schlachter
-- @copyright 2017 Uli Schlachter
-- @containermod awful.widget.only_on_screen
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local type = type
@ -130,6 +129,10 @@ capi.screen.connect_signal("property::outputs", function()
end
end)
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(only_on_screen, only_on_screen.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -7,7 +7,6 @@
-- @copyright 2009 Julien Danjou
-- @copyright 2018 Aire-One
-- @widgetmod awful.widget.prompt
-- @supermodule wibox.container.background
---------------------------------------------------------------------------
--- The prompt foreground color.
@ -159,6 +158,10 @@ function widgetprompt.mt:__call(...)
return widgetprompt.new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(widgetprompt, widgetprompt.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -70,8 +70,7 @@ function gtk.get_theme_variables()
local result = {}
local _gtk_status, Gtk = pcall(function()
local lgi = require('lgi')
return lgi.require('Gtk', '3.0')
return require('lgi').Gtk
end)
if not _gtk_status or not Gtk then
gears_debug.print_warning(

628
lib/gears/connection.lua Normal file
View File

@ -0,0 +1,628 @@
--- Object oriented way to connect objects.
--
-- All AwesomeWM objects have a `emit_signal` method.
-- They allow to attach business logic to a property value change or random
-- types of events.
--
-- The default way to attach business logic to signals is to use `connect_signal`.
-- It allows to call a function when the signal is emitted. This remains the most
-- common way to perform a connection. However, it is very verbose to use that
-- construct alongside the declarative widget system. `gears.connection` is much
-- easier to integrate in such constructs:
--
-- @DOC_wibox_decl_doc_connection4_EXAMPLE@
--
-- Limitations
-- ===========
--
-- * When used directly as a value to a declarative object
-- (`text = gears.connection{...}`), it is necessary to manually disconnect
-- the connectio if you want it to stop being auto-updated.
--
-- @author Emmanuel Lepage-Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2019-2020 Emmanuel Lepage-Vallee
-- @classmod gears.connection
local gobject = require("gears.object")
local gtable = require("gears.table")
local gtimer = require("gears.timer")
local module = {}
local function get_class(name)
if type(name) ~= "string" then return name end
return _G[name] or require(name)
end
local function gen_get_children_by_id(self)
return function(id)
return (self._private.env_ids or {})[id] or {}
end
end
local function get_env(_, cb)
local env, position = nil, nil
for i = 1, math.huge do
local name, val = debug.getupvalue(cb, i)
if name == "_ENV" then
env, position = val, i
break
elseif name == nil then
break
end
end
return env, position
end
-- Expose the declarative tree `id`s to the callback as variables.
--
-- Note that this runs into a slight conflict with the ability to
-- place a widget in multiple trees. If it happens, then obviously
-- there will be `id` conflicts. That's solvable by only using
-- `get_children_by_id` in such situation. As of now, there is no
-- code to track `id`s across trees. So the callback will only have the
-- ids from the tree it was last added to.
local function extend_env(self)
local cb, ids = self._private.callbacks[1], self._private.env_ids
if (not cb) or (not ids) then return end
self._private.env_init = true
local env, position = get_env(self, cb)
if not env then return end
local gcbi = nil
local new_env = setmetatable({}, {
__index = function(_, key)
if key == "get_children_by_id" then
gcbi = gcbi or gen_get_children_by_id(self)
return gcbi
elseif ids[key] and #ids[key] == 1 then
return ids[key][1]
end
local v = env[key]
if v then return v end
return _G[key]
end,
__newindex = function(_, key, value)
_G[key] = value
end
})
debug.setupvalue(cb, position, new_env)
end
local function set_target(self)
local p = self._private
local has_target = p.target
and p.initiate ~= false
local has_source = #p.sources >= 1
and #p.source_properties >= 1
extend_env(self)
for _, callback in ipairs(self._private.callbacks) do
local ret = callback(
p.sources[1],
p.target,
(p.sources[1] and p.source_properties[1]) and
p.sources[1][p.source_properties[1]] or nil
)
if self.target_property and self._private.target then
self._private.target[self.target_property] = ret
end
end
if p.target_method then
p.target[p.target_method]()
end
-- There isn't enough information to initiate anything yet.
if not (has_target and has_source) then return end
if p.target_property then
p.target[p.target_property] = p.sources[1][p.source_properties[1]]
end
end
-- When all properties necessary to set the initial value are set.
local function initiate(self)
if self._private.initiating then return end
-- We don't know if properties will be overriden or if a callback/method
-- will be added. Better wait.
gtimer.delayed_call(function()
-- It might have been disabled since then.
if not self._private.enabled then return end
set_target(self)
self._private.initiating = false
end)
self._private.initiating = true
end
function module:get_initiate()
return self._private.initiate
end
--- If the property should be set when the target object is defined.
--
-- It is **enabled** by default for convinience.
--
-- @DOC_text_gears_connection_initiate_EXAMPLE@
--
-- @property initiate
-- @tparam[opt=true] boolean string initiate
-- @propemits true false
function module:set_initiate(value)
if self._private.initiate == value then return end
self._private.initiate = value
self:emit_signal("property::initiate", value)
if value then
initiate(self)
end
end
--- Turn this connection on or off.
--
-- @DOC_text_gears_connection_enabled_EXAMPLE@
--
-- @property enabled
-- @tparam boolean enabled
-- @see disconnect
-- @see reconnect
-- @propemits true false
function module:get_enabled()
return self._private.enabled
end
function module:set_enabled(value)
if value == self._private.enabled then return end
self._private.enabled = value
self:emit_signal("property::enabled", value)
end
--- A list of source object signals.
--
-- @property signals
-- @tparam table signals
-- @propemits true false
-- @see signal
-- @see source_property
function module:get_signals()
return self._private.signals
end
function module:set_signals(value)
self:disconnect()
self._private.signals = value
self:reconnect()
self:emit_signal("property::signal", value[1])
self:emit_signal("property::signals", value)
initiate(self)
end
function module:get_signal()
return self._private.signals < 2 and self._private.signals[1] or nil
end
--- The (source) signal to monitor.
--
-- Note that `signals` and `source_property` are also provided to simplify
-- common use cases.
--
-- @property signal
-- @param string
-- @propemits true false
-- @see signals
-- @see source_property
function module:set_signal(signal)
self._private.signals = {signal}
end
--- The object for the right side object of the connection.
--
-- When used in a widget declarative tree, this is implicit and
-- is the parent object.
--
-- @DOC_text_gears_connection_target_EXAMPLE@
--
-- @property target
-- @tparam gears.object target
-- @propemits true false
-- @see target_property
-- @see target_method
function module:get_target()
return self._private.target
end
function module:set_target(target)
self._private.target = target
self:emit_signal("property::target", target)
initiate(self)
end
--- The target object property to set when the source property changes.
--
-- @property target_property
-- @tparam string target_property
-- @propemits true false
-- @see target
-- @see target_method
-- @see source_property
function module:get_target_property()
return self._private.target_property
end
function module:set_target_property(value)
self._private.target_property = value
self:emit_signal("property::target_property", value)
initiate(self)
end
--- Rather than use a property, call a method.
--
-- @DOC_text_gears_connection_method_EXAMPLE@
--
-- @property target_method
-- @tparam string target_method
-- @propemits true false
-- @see target
-- @see target_property
function module:get_target_method()
return self._private.target_method
end
function module:set_target_method(value)
self._private.target_method = value
self:emit_signal("property::target_method", value)
initiate(self)
end
--- Use a whole class rather than an object as source.
--
-- Many classes, like `client`, `tag`, `screen` and `naughty.notification`
-- provide class level signals. When any instance of those classes emit a
-- signal, it is forwarded to the class level signals.
--
-- @DOC_text_gears_connection_class_EXAMPLE@
--
-- @property source_class
-- @tparam class|string source_class
-- @propemits true false
-- @see source
-- @see source_property
function module:set_source_class(class)
self:disconnect()
self._private.source_class = get_class(class)
self:reconnect()
self:emit_signal("property::source_class", self._private.source_class)
end
function module:get_source_class()
return self._private.source_class
end
--- The source object (connection left hand side).
-- @property source
-- @tparam gears.object source
-- @propemits true false
-- @see sources
-- @see source_class
function module:get_source()
return self._private.sources[1]
end
function module:set_source(source)
self:disconnect()
self._private.sources = {source}
self:reconnect()
self:emit_signal("property::source", source)
self:emit_signal("property::sources", self._private.sources)
initiate(self)
end
--- The source object(s)/class property.
--
-- @property source_property
-- @tparam string source_property
-- @propemits true false
function module:get_source_property()
return #self._private.source_properties == 1 and
self._private.source_properties[1] or nil
end
function module:set_source_property(prop)
self.source_properties = {prop}
end
function module:get_source_properties()
return self._private.source_properties
end
function module:set_source_properties(props)
self:disconnect()
self._private.source_properties = props
local signals = {}
self:reconnect()
for _, prop in ipairs(props) do
table.insert(signals, "property::"..prop)
end
self.signals = signals
self:emit_signal("property::source_property", props[1])
self:emit_signal("property::source_properties", props)
end
--- A list of source objects (connection left hand side).
--
-- If many objects have the same signal, it's not necessary
-- to make multiple `gears.connection`. They can share the same.
--
-- @property sources
-- @tparam gears.object sources
-- @propemits true false
-- @see append_source_object
-- @see remove_source_object
function module:get_sources()
return self._private.sources
end
function module:set_sources(sources)
if not sources then
sources = {}
end
self:disconnect()
self._private.sources = sources
self:reconnect()
self:emit_signal("property::source", sources[1])
self:emit_signal("property::sources", sources)
initiate(self)
end
--- Add a source object.
--
-- @DOC_text_gears_connection_add_remove_EXAMPLE@
--
-- @method append_source_object
-- @tparam gears.object obj The object.
-- @see sources
-- @see remove_source_object
-- @see has_source_object
-- @see source
-- @see sources
function module:append_source_object(obj)
if self:has_source_object(obj) then return end
table.insert(self._private.sources, obj)
self:emit_signal("property::sources", self._private.sources)
if #self._private.sources == 1 then
initiate(self)
end
end
--- Remove a source object.
--
-- @method remove_source_object
-- @tparam gears.object obj The object.
-- @see sources
-- @see append_source_object
-- @see has_source_object
-- @see source
-- @see sources
function module:remove_source_object(obj)
for k, o in ipairs(self._private.sources) do
if obj == o then
table.remove(self._private.sources, k)
self:emit_signal("property::sources", self._private.sources)
return true
end
end
return false
end
--- Return true when `obj` is already a source object.
--
-- @method module:has_source_object
-- @tparam gears.object obj The object.
-- @see append_source_object
-- @see remove_source_object
-- @see source
-- @see sources
function module:has_source_object(obj)
for _, o in ipairs(self._private.sources) do
if o == obj then return true end
end
return false
end
--- A function called when the source changes.
--
--
-- @DOC_wibox_decl_doc_connection2_EXAMPLE@
--
-- The callback arguments are:
--
-- callback = function(source, target, sig_arg1, ...)
-- /\ /\ /\ /\
-- | | | |
-- The client -| | | |
-- It will be the widget -| | |
-- Signal first argument, the client -| |
-- All other signal arguments -|
--
-- @property callback
-- @tparam function callback
-- @propemits true false
function module:get_callback()
return self._private.callbacks[1]
end
function module:set_callback(cb)
self._private.callbacks = {cb}
self:emit_signal("property::callback", cb)
self._private.env_init = false
initiate(self)
end
-- When used in a declarative tree, this will be the
-- object it is initiated from. The `key` can be a number,
-- in which case we do nothing. It can also be a string,
-- in which case it becomes `target_property`
function module:_set_declarative_handler(parent, key, ids)
self.target = parent
self._private.env_ids = ids
self._private.env_init = false
if type(key) == "string" then
self.target_property = key
end
initiate(self)
end
--- Disconnect this object.
--
-- @method disconnect
-- @see reconnect
function module:disconnect()
if self._private.source_class then
for _, sig in ipairs(self._private.signals) do
self._private.source_class.disconnect_signal(
sig, self._private._callback
)
end
end
for _, src in ipairs(self._private.sources) do
for _, sig in ipairs(self._private.signals) do
src:disconnect_signal(sig, self._private._callback)
end
end
end
--- Reconnect this object.
--
-- @method reconnect
-- @see disconnect
function module:reconnect()
self:disconnect()
if self._private.source_class then
for _, sig in ipairs(self._private.signals) do
self._private.source_class.connect_signal(
sig, self._private._callback
)
end
end
for _, src in ipairs(self._private.sources) do
for _, sig in ipairs(self._private.signals) do
src:connect_signal(sig, self._private._callback)
end
end
end
--- Create a new `gears.connection` object.
--
-- @constructorfct gears.connection
-- @tparam table args
-- @tparam boolean args.initiate If the property should be set when the target object is defined.
-- @tparam boolean args.enabled Turn this connection on or off.
-- @tparam boolean args.signals A list of source object signals.
-- @tparam string args.signal The (source) signal to monitor.
-- @tparam gears.object args.target The object for the right side object of the connection.
-- @tparam string args.target_property The target object property to set when the source property changes.
-- @tparam string args.target_method Rather than use a property, call a method.
-- @tparam class|string args.source_class Use a whole class rather than an object as source.
-- @tparam gears.object args.source The source object (connection left hand side).
-- @tparam string args.source_property The source object(s)/class property.
-- @tparam gears.object args.sources A list of source objects (connection left hand side).
-- @tparam function args.callback A function called when the source changes.
local function new(_, args)
local self = gobject {
enable_properties = true,
}
rawset(self, "_private", {
enabled = true,
signals = {},
initiate = true,
sources = {},
callbacks = {},
source_properties = {},
target = nil,
_callback = function()
if not self._private.enabled then return end
set_target(self)
end
})
gtable.crush(self, module, true )
gtable.crush(self, args , false)
return self
end
--@DOC_object_COMMON@
return setmetatable(module, {__call = new})

View File

@ -172,39 +172,28 @@ end
-- @tparam string path The directory to search.
-- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }`
-- If ommited, all files are considered.
-- @tparam[opt=false] boolean absolute_path Return the absolute path instead of the filename.
-- @treturn string|nil A randomly selected filename from the specified path (with
-- a specified extension if required) or nil if no suitable file is found. If `absolute_path`
-- is set, then a path is returned instead of a file name.
-- a specified extension if required) or nil if no suitable file is found.
-- @staticfct gears.filesystem.get_random_file_from_dir
function filesystem.get_random_file_from_dir(path, exts, absolute_path)
function filesystem.get_random_file_from_dir(path, exts)
local files, valid_exts = {}, {}
-- Transforms { "jpg", ... } into { [jpg] = #, ... }
if exts then for i, j in ipairs(exts) do valid_exts[j:lower():gsub("^[.]", "")] = i end end
if exts then for i, j in ipairs(exts) do valid_exts[j:lower()] = i end end
-- Build a table of files from the path with the required extensions
local file_list = Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
-- This will happen when the directory doesn't exist.
if not file_list then return nil end
for file in function() return file_list:next_file() end do
if file:get_file_type() == "REGULAR" then
local file_name = file:get_display_name()
if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then
table.insert(files, file_name)
end
end
end
if #files == 0 then return nil end
-- Return a randomly selected filename from the file table
local file = files[math.random(#files)]
return absolute_path and (path:gsub("[/]*$", "") .. "/" .. file) or file
return #files > 0 and files[math.random(#files)] or nil
end
return filesystem

View File

@ -23,6 +23,9 @@ return
string = require("gears.string");
sort = require("gears.sort");
filesystem = require("gears.filesystem");
reactive = require("gears.reactive");
connection = require("gears.connection");
watcher = require("gears.watcher");
}
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -46,11 +46,9 @@ function object.add_signal()
end
--- Connect to a signal.
--
--@DOC_text_gears_object_signal_EXAMPLE@
--
-- @tparam string name The name of the signal.
-- @tparam function func The callback to call when the signal is emitted.
-- @tparam string name The name of the signal
-- @tparam function func The callback to call when the signal is emitted
-- @method connect_signal
function object:connect_signal(name, func)
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
@ -94,16 +92,10 @@ local function make_the_gc_obey(func)
return func
end
--- Connect to a signal weakly.
--
-- This allows the callback function to be garbage collected and
-- automatically disconnects the signal when that happens.
-- **Warning:**
-- Only use this function if you really, really, really know what you
-- are doing.
--
-- @tparam string name The name of the signal.
-- @tparam function func The callback to call when the signal is emitted.
--- Connect to a signal weakly. This allows the callback function to be garbage
-- collected and automatically disconnects the signal when that happens.
-- @tparam string name The name of the signal
-- @tparam function func The callback to call when the signal is emitted
-- @method weak_connect_signal
function object:weak_connect_signal(name, func)
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
@ -112,9 +104,9 @@ function object:weak_connect_signal(name, func)
sig.weak[func] = make_the_gc_obey(func)
end
--- Disonnect from a signal.
-- @tparam string name The name of the signal.
-- @tparam function func The callback that should be disconnected.
--- Disonnect to a signal.
-- @tparam string name The name of the signal
-- @tparam function func The callback that should be disconnected
-- @method disconnect_signal
function object:disconnect_signal(name, func)
local sig = find_signal(self, name)
@ -126,8 +118,8 @@ end
--
-- @tparam string name The name of the signal
-- @param ... Extra arguments for the callback functions. Each connected
-- function receives the object as first argument and then any extra
-- arguments that are given to emit_signal()
-- function receives the object as first argument and then any extra arguments
-- that are given to emit_signal()
-- @method emit_signal
function object:emit_signal(name, ...)
local sig = find_signal(self, name)

656
lib/gears/reactive.lua Normal file
View File

@ -0,0 +1,656 @@
--- Utility module to convert functions to Excel like objects.
--
-- When converting a function into a `gears.reactive` object, all
-- properties accessed by the function are monitored for changes.
--
-- When such a change is detected, the `property::value` signal is emitted. When
-- used within a widget declarative construct, the property it is attached
-- to will also be automatically updated.
--
-- Theory
-- ======
--
-- ![Client geometry](../images/gears_reactive.svg)
--
-- To use this module and, more importantly, to understand how to write something
-- that actually works based on it, some background is required. Most AwesomeWM
-- objects are based on the `gears.object` module or it's `C` equivalent. Those
-- objects have "properties". Behind the scene, they have getters, setters and
-- a signal to notify the value changed.
--
-- `gears.reactive` adds a firewall like sandbox around the function. It
-- intercept any `gears.object` instance trying to cross the sandbox boundary
-- and replace them with proxy objects. Those proxies have built-in
-- introspection code to detect how they are used. This is then converted into
-- a list of objects and signals to monitor. Once one of the monitored object
-- emits one of the monitored signal, then the whole function is re-evaluated.
-- Each time the function is evaluated, the "target" properties are updated. The
-- reactive function result or any call to external function from within goes
-- through the firewall again and any proxies are removed.
--
-- That design has one big limitation. It cannot detect any changes which are
-- not directly part of `gears.object` instance. You cannot use random tables
-- and expect the function to be called when it's content change. To work
-- around this, it is recommanded to make "hub" objects to store the data used
-- within the reactive function.
--
-- Recommanded usage
-- =================
--
-- The best way to use `gears.reactive` is when the value used within the
-- expressions are part of other objects. It can be a `gears.watcher`, but
-- it can also be a custom object:
--
-- @DOC_wibox_widget_declarative_reactive_EXAMPLE@
--
-- Limitations
-- ===========
--
-- `gears.reactive` is pushing Lua way beyond what it has been designed for.
-- Because of this, there is some limitations.
--
-- * This module will **NOT** try to track the change of other
-- functions and methods called by the expression. It is **NOT** recursive
-- and only the top level properties are tracked. This is a feature, not a
-- bug. If it was recursive, this list of limitations or gotchas would be
-- endless.
-- * This only works with `gears.object` and Core API objects which implement
-- the `property::*****` signals correctly. If it is a regular Lua table
-- or the property signals are incorrectly used, the value changes cannot
-- be detected. If you find something that should work, but doesn't in
-- one of the AwesomeWM API, [please report a bug](https://github.com/awesomeWM/awesome/issues/new).
-- * More generally, when making a custom `gears.object` with custom setters,
-- it is the programmer responsibility to emit the signal. It is also
-- required to only emit those signals when the property actually changes to
-- avoid an unecessary re-evaluations.
-- * Changing the type of the variables accessed by the reactive function
-- (its "upvalues") after the reactive expression has been created wont
-- be detected. It will cause missed updates and, potentially, hard to debug
-- Lua errors within the proxy code itself.
-- * Internally, the engine tries its best to prevent the internal proxy objects
-- to leak out the sandbox. However this cannot be perfect, at least not
-- without adding limitation elsewhere. It is probably worth reporting a bug if
-- you encounter such an issue. But set your expectations, not all corner case
-- can be fixed.
-- * Don't use rawset in the expression.
-- * If the return value is a table, only the first 3 nesting levels are sanitized.
-- Avoid using nested tables in the returned value if you can. `gears.object`
-- instances *should* be fine.
-- * There is currently no way to disable a reactive expression once it's
-- been defined. This may change eventually.
-- * Rio Lua 5.1 (not LuaJIT 2.1) is currently not support. If you need it,
-- [please report a bug](https://github.com/awesomeWM/awesome/issues/new).
--
-- @author Emmanuel Lepage-Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2017-2020 Emmanuel Lepage-Vallee
-- @classmod gears.reactive
-- This file is provided under the BSD 2 clause license since it is better than
-- any existing implementation (most of which only support Lua 5.1).
--
-- Copyright 2020 Emmanuel Lepage-Vallee &lt;elv1313@gmail.com&gt;
--
-- Redistribution and use in source and binary forms, with or without modification,
-- are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice, this
-- list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright notice,
-- this list of conditions and the following disclaimer in the documentation
-- and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
local gobject = require("gears.object")
local gtable = require("gears.table")
local gtimer = require("gears.timer")
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
-- It's only possible to know when something changes if it has signals.
local function is_gears_object(input)
return type(input) == "table" and rawget(input, "emit_signal")
end
-- If the sandbox proxies were to leave the reactive expressions, bad
-- things would happen. Catch functions and constructors to make sure
-- we strip away the proxy.
local function is_callable(object)
local t = type(object)
if t == "function" then return true end
if not (t == "table" or t == "userdata") then return false end
local mt = getmetatable(object)
return mt and mt.__call ~= nil
end
-- Get the upvalue current value or cached value.
local function get_upvalue(proxy_md)
if type(proxy_md) ~= "table" then return proxy_md end
return proxy_md._reactive.getter and
proxy_md._reactive.getter() or proxy_md._reactive.input
end
-- Attempt to remove the proxy references. There might be loops
-- and stackoverflows, so it will only go 3 level deep. With some
-- luck, the result wont be a table. Even then, hopefully it wont
-- contain "raw" sub-tables. If it contains `gears.object`, it's
-- safe since they will be proxied.
local sanitize_return = nil
sanitize_return = function(input, depth)
if (depth or 0) > 3 then return input end
-- The first `input` is always a table, so it is safe to call `pairs`.
for k, v in pairs(input) do
local t = type(v)
if t == "table" then
if v._reactive then
-- No need to go deeper unless the user used rawset.
input[k] = get_upvalue(v)
else
-- It's an unproxied raw table, dig further.
sanitize_return(v, (depth or 1) + 1)
end
end
end
return input
end
-- The actual code that connects the gears.object signals.
local function add_key(self, key)
if type(key) ~= "string" or not is_gears_object(get_upvalue(self)) then return end
local input, objs = get_upvalue(self), self._reactive.origin.objects
objs[input] = objs[input] or {}
if objs[input][key] then return end
objs[input][key] = true
-- Prefer the `weak` version to avoid memory leaks. If nothing is holding a
-- reference, then the value wont change. Yes, there is some hairy corner cases.
-- Yes, the expression proxy itself is holding a strong reference so this wont
-- get used very often.
if input.weak_connect_signal then
input:weak_connect_signal("property::"..key, self._reactive.origin.callback)
else
input:connect_signal("property::"..key, self._reactive.origin.callback)
end
end
local function gen_proxy_md(input, origin, getter)
assert(input and origin)
return {
input = (not getter) and input or nil,
getter = getter,
children = {},
properties = {},
origin = origin
}
end
local create_proxy = nil
-- Un-proxy the function input and proxy its output.
local function proxy_call(self, ...)
local newargs, newrets = {}, {}
-- Remove the proxies before calling the function.
for _, v in ipairs{...} do
if type(v) == "table" and v._reactive then
v = get_upvalue(v)
end
table.insert(newargs, v)
end
local rets
if #newargs > 0 then
rets = {get_upvalue(self)(unpack(newargs))}
else
rets = {get_upvalue(self)(nil)}
end
-- Add new proxies to make sure changes are detected if something
-- like `a_function().foo` is used. Since we still have legacy accessor
-- implemented as method (t:clients(), c:tags(), etc), this is actually
-- likely to happen.
for _, v in ipairs(rets) do
local ret, _ = create_proxy(v, self._reactive.origin)
table.insert(newrets, ret)
end
return unpack(newrets)
end
-- Build a tree of proxies or return the primitives.
local function proxy_index(self, key)
local up = get_upvalue(self)
-- Since it would otherwise print a cryptic error.
assert(
type(up) == "table",
"Trying to index a "..type(up).." value in a `gears.reactive` expression."
)
local upk = up[key]
-- Connect.
add_key(self, key)
local ret, is_proxy = create_proxy(upk, self._reactive.origin)
-- Always query the non-proxy upvalue. We cannot detect if they
-- change.
if is_proxy then
rawset(self, key, ret)
end
return ret
end
-- Set valuers trough the proxy.
local function proxy_newindex(self, key, value)
rawset(self, key, create_proxy(value, self._reactive.origin))
-- Strip the proxy before setting the value on the original object.
if type(value) == "table" and value._reactive then
value = get_upvalue(value)
end
local v = get_upvalue(self)
v[key] = value
-- Connect.
if is_gears_object(v) and not self._reactive.origin.objects[self][key] then
add_key(self, key)
end
end
-- It's possible that multiple proxy points to the same value.
-- While we could make a large map of all proxies, it's simpler
-- to just implement __eq/__lt/__le and be done.
local function proxy_equal(a, b)
a, b = get_upvalue(a), get_upvalue(b)
return a == b
end
local function proxy_lesser(a, b)
a, b = get_upvalue(a), get_upvalue(b)
return a < b
end
local function proxy_lesser_equal(a, b)
a, b = get_upvalue(a), get_upvalue(b)
return a <= b
end
local function proxy_tostring(o)
return tostring(get_upvalue(o))
end
-- Wrap tables and functions into a proxy object.
create_proxy = function(input, origin, getter)
local t = type(input)
local is_call = is_callable(input)
-- Everything but the tables are immutable.
if t ~= "table" and not is_call then return input, false end
-- Remove any foreign proxy.
if t ~= "function" and input._reactive and input._reactive.origin ~= origin then
input = get_upvalue(input)
end
return setmetatable({_reactive = gen_proxy_md(input, origin, getter)}, {
__index = proxy_index,
__newindex = proxy_newindex,
__call = proxy_call,
__eq = proxy_equal,
__lt = proxy_lesser,
__le = proxy_lesser_equal,
__tostring = proxy_tostring
}), true
end
-- Create a "fake" upvalue ref because otherwise the sandbox could
-- "leak" and set upvalue in actual code outside of the function.
-- By "leak", I mean the proxy would become visible from outside
-- of the sandbox by the reactive expression "siblings" sharing the
-- same environment.
local function copy_upvalue_reference(loaded, idx, value)
-- Make sure it has a fresh upvalue reference. Something that
-- we are sure cannot be shared with something else.
local function fake_upvalue_env()
return value
end
debug.upvaluejoin(loaded, idx, fake_upvalue_env, 1) --luacheck: ignore
end
-- Create a meta-getter for the original `fct` upvalue at index `idx`.
-- This will allow all the other code to dynamically get a "current" version
-- of the upvalue rather than something from the cache. It will also avoid
-- having to store a strong reference to the original value.
local function create_upvalue_getter(fct, idx)
local placeholder = nil
local function fake_upvalue_getter()
return placeholder
end
-- Using this, each time `fake_upvalue_getter` is called, it
-- will return the current upvalue. This means we don't have to
-- cache it. Thus, the cache cannot get outdated. The drawback if
-- that if the type changes from an object to a primitive, it
-- will explode.
debug.upvaluejoin(fake_upvalue_getter, 1, fct, idx) --luacheck: ignore
return fake_upvalue_getter
end
-- Create a copy of the function and replace it's environment.
local function sandbox(fct, env, vars, values)
-- We must serialize the function for several reasons. First of all, it
-- might get wrapped into multiple `gears.reactive` objects (which we should
-- handle differently, but currently allow) or share the "upvalue environemnt"
-- (this variables from the upper execution context and stack frames).
--
-- For example, if 2 function both access the variables "foo" and "bar"
-- from the global context, they might end up with the same execution
-- environment. If we didn't create a copy, calling `debug.upvaluejoin`
-- woulc affect both functions.
local dump = string.dump(fct)
local loaded = load(dump, nil, nil, env)
-- It doesn't seem possible to "just remove" the upvalues. It's not possible
-- to have the catch-all in the metatable. It would have been nicer since this
-- code is redundant with the metatable (which are still needed for _G and _ENV
-- content).
for name, k in pairs(vars) do
if is_callable(values[name]) or type(values[name]) == "table" then
-- For table, functions and objects, use a proxy upvalue.
copy_upvalue_reference(
loaded,
k,
create_proxy(values[name], env.__origin, create_upvalue_getter(fct ,k))
)
else
-- For string, booleans, numbers and function, use the real upvalue.
-- This means if it is changed by something else, the sandboxed
-- copy sees the change.
debug.upvaluejoin(loaded, k, fct, k) --luacheck: ignore
end
end
return loaded
end
-- `getfenv` and `setfenv` would simplify this a lot, but are not
-- present in newer versions of Lua. Technically, we could have 3
-- implementation optimized for each Lua versions, but this one
-- seems to be portable (until now...). So while the performance
-- isn't as good as it could be, it's maintainable.
local function getfenv_compat(fct, root)
local vars, soft_env = {}, {}
local origin = {
objects = {},
callback = function()
root:emit_signal("property::value")
end
}
for i = 1, math.huge do
local name, val = debug.getupvalue(fct, i)
if name == nil then break end
soft_env[name] = val
if not vars[name] then
vars[name] = i
end
end
-- Create the sandbox.
local self = {__origin = origin}
local sandboxed = sandbox(fct, self, vars, soft_env)
return setmetatable(self, {
__index = function(_, key)
if _G[key] then
return create_proxy(_G[key], origin)
end
return soft_env[key]
end,
__newindex = function(_, key, value)
-- This `if` might be dead code.
if vars[key] then
debug.setupvalue(sandboxed, vars[key], value)
-- Do not try to disconnect the old one. It would make the code too complex.
if (not soft_env[key]) or get_upvalue(soft_env[key]) ~= value then
soft_env[key] = create_proxy(value, origin)
end
else
rawset(vars, key, create_proxy(value, origin))
end
end,
__call = function()
return unpack(sanitize_return({sandboxed()}))
end
})
end
local module = {}
local function real_set_value(self, force)
if self._private.delayed_started and not force then return end
local function value_transaction()
-- If `get_value` was called, then this transaction is no longer
-- pending.
if not self._private.delayed_started then return end
-- Reset the delay in case the setter causes the expression to
-- change.
self._private.delayed_started = false
self._private.value = self._private.origin()
self._private.evaluated = true
for _, target in pairs(self._private.targets) do
local obj, property = target[1], target[2]
obj[property] = self._private.value
end
end
if self._private.delayed and not force then
gtimer.delayed_call(value_transaction)
self._private.delayed_started = true
else
self._private.delayed_started = true
value_transaction()
end
end
--- A function which will be evaluated each time its content changes.
--
-- @property expression
-- @param function
-- @propemits true false
--- The current value of the expression.
-- @property value
-- @propemits false false
--- Only evaluate once per event loop iteration.
--
-- In most case this is a simple performance win, but there is some
-- case where you might want the expression to be evaluated each time
-- one of the upvalue "really" change rather than batch them. This
-- option is enabled by default.
--
-- @property delayed
-- @tparam[opt=true] boolean delayed
-- @propemits true false
function module:get_delayed()
return self._private.delayed
end
function module:set_delayed(value)
if value == self._private.delayed then return end
self._private.delayed = value
self:emit_signal("property::delayed", value)
end
function module:set_expression(value)
self:disconnect()
self._private.origin = getfenv_compat(value, self)
self:connect_signal("property::value", real_set_value)
end
function module:get_value()
-- This will call `real_set_value`.
if (not self._private.evaluated) and self._private.origin then
self:refresh()
end
if self._private.delayed_started then
real_set_value(self, true)
end
return self._private.value
end
function module:set_value()
assert(false, "A value cannot be set on a `gears.reactive` instance.")
end
--- Disconnect all expression signals.
--
-- @method disconnect
function module:disconnect()
if self._private.origin then
for obj, properties in pairs(self._private.origin.__origin.objects) do
for property in pairs(properties) do
obj:disconnect_signal("property::"..property, self._private.origin.__origin.callback)
end
end
end
end
--- Recompute the expression.
--
-- When the expression uses a non-object upvalue, the changes cannot
-- be auto-retected. Calling `:refresh()` will immediatly recompute the
-- expression.
--
-- @method refresh
function module:refresh()
if self._private.origin then
self:emit_signal("property::value")
end
end
-- Add a new target property and object.
function module:_add_target(object, property)
local hash = tostring(object)..property
if self._private.targets[hash] then return end
self._private.targets[hash] = {object, property}
self:emit_signal("target_added", object, property)
if self._private.evaluated then
object[property] = self._private.value
else
real_set_value(self)
end
end
--- Emitted when a new property is attached to this reactive expression.
--
-- @signal target_added
-- @tparam gears.object object The object (often the widget).
-- @tparam string property The property name.
function module:_set_declarative_handler(parent, key)
-- Lua "properties", aka table.foo must be strings.
assert(
type(key) == "string",
"gears.reactive can only be used ob object properties"
)
self:_add_target(parent, key)
end
--- Create a new `gears.reactive` object.
-- @constructorfct gears.reactive
-- @tparam table args
-- @tparam function args.expression A function which accesses other `gears.object`.
-- @tparam gears.object args.object Any AwesomeWM object.
-- @tparam string args.property The name of the property to track.
local function new(_, args)
-- It *can* be done. Actually, it is much easier to implement this module
-- on 5.1 since `setfenv` is much simpler than `debug.upvaluejoin`. However,
-- unless someone asks, then why support 2 incompatible code paths. Luajit 2.1
-- supports `debug.upvaluejoin`.
assert(
debug.upvaluejoin, --luacheck: ignore
"Sorry, `gears.reactive` doesn't support Lua 5.1 at this time"
)
if type(args) == "function" then
args = {expression = args}
end
local self = gobject {
enable_properties = true,
}
rawset(self, "_private", {
targets = {},
delayed = true
})
gtable.crush(self, module, true)
gtable.crush(self, args, false)
self:connect_signal("property::value", real_set_value)
return self
end
--@DOC_object_COMMON@
return setmetatable(module, {__call = new})

View File

@ -39,31 +39,27 @@ function gtable.join(...)
return ret
end
--- Override elements in the target table with values from the source table.
--- Override elements in the first table by the one in the second.
--
-- Note that this method doesn't copy entries found in `__index`.
-- Nested tables are copied by reference and not recursed into.
--
-- @tparam table target The target table. Values from `source` will be copied
-- into this table.
-- @tparam table source The source table. Its values will be copied into
-- `target`.
-- @tparam[opt=false] bool raw If `true`, values will be assigned with `rawset`.
-- This will bypass metamethods on `target`.
-- @treturn table The target table.
-- @tparam table t the table to be overriden
-- @tparam table set the table used to override members of `t`
-- @tparam[opt=false] bool raw Use rawset (avoid the metatable)
-- @treturn table t (for convenience)
-- @staticfct gears.table.crush
function gtable.crush(target, source, raw)
function gtable.crush(t, set, raw)
if raw then
for k, v in pairs(source) do
rawset(target, k, v)
for k, v in pairs(set) do
rawset(t, k, v)
end
else
for k, v in pairs(source) do
target[k] = v
for k, v in pairs(set) do
t[k] = v
end
end
return target
return t
end
--- Pack all elements with an integer key into a new table.
@ -169,23 +165,6 @@ function gtable.keys(t)
return keys
end
--- Get the number of keys in a table, both integer and string indicies.
--
-- This is functionally equivalent, but faster than `#gears.table.keys(t)`.
--
-- @DOC_text_gears_table_count_keys_EXAMPLE@
--
-- @tparam table t The table for which to count the keys.
-- @treturn number The number of keys in the table.
-- @staticfct gears.table.count_keys
function gtable.count_keys(t)
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
end
--- Filter a table's keys for certain content type.
--
-- @tparam table t The table to retrieve the keys for.
@ -229,8 +208,7 @@ end
--- Clone a table.
--
-- @tparam table t The table to clone.
-- @tparam[opt=true] bool deep If `true`, recurse into nested tables to create
-- a deep clone.
-- @tparam[opt=true] bool deep Create a deep clone?
-- @treturn table A clone of `t`.
-- @staticfct gears.table.clone
function gtable.clone(t, deep)
@ -252,16 +230,14 @@ end
-- `first_index` has to be specified.
--
-- @tparam table t The input table.
-- @param value The start value. Must be an element of the input table `t`.
-- @tparam[opt=1] number step_size The amount to increment the index by.
-- When this is negative, the function will cycle through the table backwards.
-- @tparam[opt=nil] function filter An optional filter function. It receives a
-- value from the table as parameter and should return a boolean. If it
-- returns `false`, the value is skipped and `cycle_value` tries the next one.
-- @param value A value from the table.
-- @tparam[opt=1] number step_size How many element forward (or backward) to pick.
-- @tparam[opt=nil] function filter An optional function. When it returns
-- `false`, the element are skipped until a match if found. It takes the value
-- as its sole parameter.
-- @tparam[opt=1] number start_at Where to start the lookup from.
-- @return The next eligible value. If no value matches, `nil` is returned.
-- @treturn number|nil If a value is found, this is its index within the input
-- table.
-- @return The value. If no element match, then `nil` is returned.
-- @treturn number|nil The element (if any) key.
-- @staticfct gears.table.cycle_value
function gtable.cycle_value(t, value, step_size, filter, start_at)
local k = gtable.hasitem(t, value, true, start_at)
@ -311,23 +287,17 @@ function gtable.iterate(t, filter, start)
end
end
--- Merge items from the source table into the target table.
--- Merge items from one table to another one.
--
-- Note that this only considers the array part of `source` (same semantics
-- as `ipairs`).
-- Nested tables are copied by reference and not recursed into.
--
-- @tparam table target The target table. Values from `source` will be copied
-- into this table.
-- @tparam table source The source table. Its values will be copied into
-- `target`.
-- @treturn table The target table.
-- @tparam table t The container table
-- @tparam table set The mixin table.
-- @treturn table (for convenience).
-- @staticfct gears.table.merge
function gtable.merge(target, source)
for _, v in ipairs(source) do
table.insert(target, v)
function gtable.merge(t, set)
for _, v in ipairs(set) do
table.insert(t, v)
end
return target
return t
end
--- Update the `target` table with entries from the `new` table.
@ -414,7 +384,7 @@ end
--- Map a function to a table.
--
-- The function is applied to each value in the table, returning a modified
-- The function is applied to each value on the table, returning a modified
-- table.
--
-- @tparam function f The function to be applied to each value in the table.
@ -430,5 +400,3 @@ function gtable.map(f, tbl)
end
return gtable
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -59,6 +59,7 @@ local traceback = debug.traceback
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local glib = require("lgi").GLib
local object = require("gears.object")
local gtable = require("gears.table")
local protected_call = require("gears.protected_call")
local gdebug = require("gears.debug")
@ -87,11 +88,11 @@ local timer = { mt = {} }
-- @method start
-- @emits start
function timer:start()
if self.data.source_id ~= nil then
if self._private.source_id ~= nil then
gdebug.print_error(traceback("timer already started"))
return
end
self.data.source_id = glib.timeout_add(glib.PRIORITY_DEFAULT, self.data.timeout * 1000, function()
self._private.source_id = glib.timeout_add(glib.PRIORITY_DEFAULT, self._private.timeout * 1000, function()
protected_call(self.emit_signal, self, "timeout")
return true
end)
@ -99,17 +100,15 @@ function timer:start()
end
--- Stop the timer.
--
-- Does nothing if the timer isn't running.
--
-- @method stop
-- @emits stop
function timer:stop()
if self.data.source_id == nil then
if self._private.source_id == nil then
gdebug.print_error(traceback("timer not started"))
return
end
glib.source_remove(self.data.source_id)
self.data.source_id = nil
glib.source_remove(self._private.source_id)
self._private.source_id = nil
self:emit_signal("stop")
end
@ -120,7 +119,7 @@ end
-- @emits start
-- @emits stop
function timer:again()
if self.data.source_id ~= nil then
if self._private.source_id ~= nil then
self:stop()
end
self:start()
@ -136,24 +135,28 @@ end
-- @param number
-- @propemits true false
local timer_instance_mt = {
__index = function(self, property)
if property == "timeout" then
return self.data.timeout
elseif property == "started" then
return self.data.source_id ~= nil
function timer:get_timeout()
return self._private.timeout
end
return timer[property]
end,
function timer:get_started()
return self._private.source_id ~= nil
end
__newindex = function(self, property, value)
if property == "timeout" then
self.data.timeout = tonumber(value)
function timer:set_started(value)
if value == self:get_started() then return end
if value then
self:start()
else
self:stop()
end
end
function timer:set_timeout(value)
self._private.timeout = tonumber(value)
self:emit_signal("property::timeout", value)
end
end
}
--- Create a new timer object.
-- @tparam table args Arguments.
@ -167,10 +170,33 @@ local timer_instance_mt = {
-- @constructorfct gears.timer
function timer.new(args)
args = args or {}
local ret = object()
local ret = object {
enable_properties = true,
enable_auto_signals = true,
}
ret.data = { timeout = 0 } --TODO v5 rename to ._private
setmetatable(ret, timer_instance_mt)
gtable.crush(ret, timer, true)
rawset(ret, "_private", { timeout = 0 })
-- Preserve backward compatibility with Awesome 4.0-4.3 use of "data"
-- rather then "_private".
rawset(ret, "data", setmetatable({}, {
__index = function(_, key)
gdebug.deprecate(
"gears.timer.data is deprecated, use normal properties",
{deprecated_in=5}
)
return ret._private[key]
end,
__newindex = function(_, key, value)
gdebug.deprecate(
"gears.timer.data is deprecated, use normal properties",
{deprecated_in=5}
)
ret._private[key] = value
end
}))
for k, v in pairs(args) do
ret[k] = v
@ -194,17 +220,13 @@ function timer.new(args)
return ret
end
--- Create a simple timer for calling the `callback` function continuously.
--
-- This is a small wrapper around `gears.timer`, that creates a timer based on
-- `callback`.
-- The timer will run continuously and call `callback` every `timeout` seconds.
-- It is stopped when `callback` returns `false`, when `callback` throws an
-- error or when the `:stop()` method is called on the return value.
--
--- Create a timeout for calling some callback function.
-- When the callback function returns true, it will be called again after the
-- same timeout. If false is returned, no more calls will be done. If the
-- callback function causes an error, no more calls are done.
-- @tparam number timeout Timeout in seconds (e.g. 1.5).
-- @tparam function callback Function to run.
-- @treturn timer The new timer object.
-- @treturn timer The timer object that was set up.
-- @staticfct gears.timer.start_new
-- @see gears.timer.weak_start_new
function timer.start_new(timeout, callback)
@ -219,18 +241,14 @@ function timer.start_new(timeout, callback)
return t
end
--- Create a simple timer for calling the `callback` function continuously.
--
-- This function is almost identical to `gears.timer.start_new`. The only
-- difference is that this does not prevent the callback function from being
-- garbage collected.
-- In addition to the conditions in `gears.timer.start_new`,
-- this timer will also stop if `callback` was garbage collected since the
-- previous run.
--
--- Create a timeout for calling some callback function.
-- This function is almost identical to `gears.timer.start_new`. The only difference
-- is that this does not prevent the callback function from being garbage
-- collected. After the callback function was collected, the timer returned
-- will automatically be stopped.
-- @tparam number timeout Timeout in seconds (e.g. 1.5).
-- @tparam function callback Function to start.
-- @treturn timer The new timer object.
-- @treturn timer The timer object that was set up.
-- @staticfct gears.timer.weak_start_new
-- @see gears.timer.start_new
function timer.weak_start_new(timeout, callback)
@ -257,7 +275,7 @@ function timer.run_delayed_calls_now()
delayed_calls = {}
end
--- Call the given function at the end of the current GLib event loop iteration.
--- Call the given function at the end of the current main loop iteration
-- @tparam function callback The function that should be called
-- @param ... Arguments to the callback function
-- @staticfct gears.timer.delayed_call

View File

@ -1,7 +1,22 @@
---------------------------------------------------------------------------
-- Functions for setting the wallpaper.
--
-- This module is deprecated, please use `awful.wallpaper`.
-- There are two levels of functionality provided by this module:
--
-- The low-level functionality consists of two functions.
-- @{set} an already-prepared wallpaper on all screens and @{prepare_context}
-- prepares things to draw a new wallpaper.
--
-- The low-level API can for example be used to set solid red as a wallpaper
-- (see @{gears.color} for details on the supported syntax):
--
-- gears.wallpaper.set("#ff0000")
--
-- Ontop of these low-level functions, the remaining functions implement more
-- useful functionality. For example, given a screen object `s`, an image can be
-- set as the wallpaper as follows:
--
-- gears.wallpaper.maximized("path/to/image.png", s)
--
-- @author Uli Schlachter
-- @copyright 2012 Uli Schlachter
@ -36,10 +51,8 @@ end
-- @param s The screen to set the wallpaper on or nil for all screens
-- @return[1] The available geometry (table with entries width and height)
-- @return[1] A cairo context that the wallpaper should be drawn to.
-- @deprecated gears.wallpaper.prepare_context
-- @staticfct gears.wallpaper.prepare_context
function wallpaper.prepare_context(s)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
s = get_screen(s)
local root_width, root_height = root.size()
@ -97,10 +110,8 @@ end
-- @param pattern The wallpaper that should be set. This can be a cairo surface,
-- a description for gears.color or a cairo pattern.
-- @see gears.color
-- @deprecated gears.wallpaper.set
-- @staticfct gears.wallpaper.set
function wallpaper.set(pattern)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
if cairo.Surface:is_type_of(pattern) then
pattern = cairo.Pattern.create_for_surface(pattern)
end
@ -110,7 +121,7 @@ function wallpaper.set(pattern)
if not cairo.Pattern:is_type_of(pattern) then
error("wallpaper.set() called with an invalid argument")
end
root.wallpaper(pattern)
root.wallpaper(pattern._native)
end
--- Set a centered wallpaper.
@ -121,10 +132,8 @@ end
-- gears.color. The default is black.
-- @param scale The scale factor for the wallpaper. Default is 1 (original size).
-- @see gears.color
-- @deprecated gears.wallpaper.centered
-- @staticfct gears.wallpaper.centered
function wallpaper.centered(surf, s, background, scale)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local geom, cr = wallpaper.prepare_context(s)
local original_surf = surf
surf = surface.load_uncached(surf)
@ -163,10 +172,8 @@ end
-- @param s The screen whose wallpaper should be set. Can be nil, in which case
-- all screens are set.
-- @param offset This can be set to a table with entries x and y.
-- @deprecated gears.wallpaper.tiled
-- @staticfct gears.wallpaper.tiled
function wallpaper.tiled(surf, s, offset)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local _, cr = wallpaper.prepare_context(s)
if offset then
@ -195,10 +202,8 @@ end
-- @param ignore_aspect If this is true, the image's aspect ratio is ignored.
-- The default is to honor the aspect ratio.
-- @param offset This can be set to a table with entries x and y.
-- @deprecated gears.wallpaper.maximized
-- @staticfct gears.wallpaper.maximized
function wallpaper.maximized(surf, s, ignore_aspect, offset)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local geom, cr = wallpaper.prepare_context(s)
local original_surf = surf
surf = surface.load_uncached(surf)
@ -238,10 +243,8 @@ end
-- @param background The background color that should be used. Gets handled via
-- gears.color. The default is black.
-- @see gears.color
-- @deprecated gears.wallpaper.fit
-- @staticfct gears.wallpaper.fit
function wallpaper.fit(surf, s, background)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local geom, cr = wallpaper.prepare_context(s)
local original_surf = surf
surf = surface.load_uncached(surf)

733
lib/gears/watcher.lua Normal file
View File

@ -0,0 +1,733 @@
--- Fetch information at a specific interval.
--
-- @author Emmanuel Lepage-Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2020 Emmanuel Lepage-Vallee
-- @classmod gears.watcher
local capi = {awesome = awesome}
local protected_call = require("gears.protected_call")
local gtimer = require("gears.timer")
local gtable = require("gears.table")
local lgi = require("lgi")
local Gio = lgi.Gio
local GLib = lgi.GLib
local module = {}
-- This is awful.util.shell
module._shell = os.getenv("SHELL") or "/bin/sh"
local end_of_file
do
-- API changes, bug fixes and lots of fun. Figure out how a EOF is signalled.
local input
if not pcall(function()
-- No idea when this API changed, but some versions expect a string,
-- others a table with some special(?) entries
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data(""))
end) then
input = Gio.DataInputStream.new(Gio.MemoryInputStream.new_from_data({}))
end
local line, length = input:read_line()
if not line then
-- Fixed in 2016: NULL on the C side is transformed to nil in Lua
end_of_file = function(arg)
return not arg
end
elseif tostring(line) == "" and #line ~= length then
-- "Historic" behaviour for end-of-file:
-- - NULL is turned into an empty string
-- - The length variable is not initialized
-- It's highly unlikely that the uninitialized variable has value zero.
-- Use this hack to detect EOF.
end_of_file = function(arg1, arg2)
return #arg1 ~= arg2
end
else
assert(tostring(line) == "", "Cannot determine how to detect EOF")
-- The above uninitialized variable was fixed and thus length is
-- always 0 when line is NULL in C. We cannot tell apart an empty line and
-- EOF in this case.
require("gears.debug").print_warning("Cannot reliably detect EOF on an "
.. "GIOInputStream with this LGI version")
end_of_file = function(arg)
return tostring(arg) == ""
end
end
end
module._end_of_file = end_of_file
function module._read_lines(input_stream, line_callback, done_callback, close)
local stream = Gio.DataInputStream.new(input_stream)
local function done()
if close then
stream:close()
end
stream:set_buffer_size(0)
if done_callback then
protected_call(done_callback)
end
end
local start_read, finish_read
start_read = function()
stream:read_line_async(GLib.PRIORITY_DEFAULT, nil, finish_read)
end
finish_read = function(obj, res)
local line, length = obj:read_line_finish(res)
if type(length) ~= "number" then
-- Error
print("Error in awful.spawn.read_lines:", tostring(length))
done()
elseif end_of_file(line, length) then
-- End of file
done()
else
-- Read a line
-- This needs tostring() for older lgi versions which returned
-- "GLib.Bytes" instead of Lua strings (I guess)
protected_call(line_callback, tostring(line))
-- Read the next line
start_read()
end
end
start_read()
end
function module._with_line_callback(cmd, callbacks)
local stdout_callback, stderr_callback, done_callback, exit_callback =
callbacks.stdout, callbacks.stderr, callbacks.output_done, callbacks.exit
local have_stdout, have_stderr = stdout_callback ~= nil, stderr_callback ~= nil
local pid, _, stdin, stdout, stderr = capi.awesome.spawn(cmd,
false, false, have_stdout, have_stderr, exit_callback)
if type(pid) == "string" then
-- Error
return pid
end
local done_before = false
local function step_done()
if have_stdout and have_stderr and not done_before then
done_before = true
return
end
if done_callback then
done_callback()
end
end
if have_stdout then
module._read_lines(Gio.UnixInputStream.new(stdout, true),
stdout_callback, step_done, true)
end
if have_stderr then
module._read_lines(Gio.UnixInputStream.new(stderr, true),
stderr_callback, step_done, true)
end
assert(stdin == nil)
return pid
end
function module._easy_async(cmd, callback)
local stdout = ''
local stderr = ''
local exitcode, exitreason
local function parse_stdout(str)
stdout = stdout .. str .. "\n"
end
local function parse_stderr(str)
stderr = stderr .. str .. "\n"
end
local function done_callback()
return callback(stdout, stderr, exitreason, exitcode)
end
local exit_callback_fired = false
local output_done_callback_fired = false
local function exit_callback(reason, code)
exitcode = code
exitreason = reason
exit_callback_fired = true
if output_done_callback_fired then
return done_callback()
end
end
local function output_done_callback()
output_done_callback_fired = true
if exit_callback_fired then
return done_callback()
end
end
return module._with_line_callback(
cmd, {
stdout=parse_stdout,
stderr=parse_stderr,
exit=exit_callback,
output_done=output_done_callback
})
end
function module._read_async(path, callback, fail_callback)
local cancel = Gio.Cancellable()
Gio.File.new_for_path(path):load_contents_async(cancel, function(file, task)
local content = file:load_contents_finish(task)
if content then
callback(path, content)
elseif fail_callback then
fail_callback(path)
end
end)
return cancel
end
-- Posix files and commands always end with a newline.
-- It is nearly always unwanted, so we strip it by default.
local function remove_posix_extra_newline(content)
-- Remove the trailing `\n`
if content:sub(-1) == '\n' then
content = content:sub(1, -2)
end
return content
end
--- Make sure we sort transactions in a way obsolete ones are
-- not used.
local function add_transaction(self, transaction)
table.insert(self._private.transactions, transaction)
end
local function remove_transaction(self, transaction)
-- Too late, abort.
for _, cancel in pairs(transaction.pending) do
cancel:cancel()
end
for k, t in ipairs(self._private.transactions) do
if t == transaction then
table.remove(self._private.transactions, k)
end
end
end
-- Keys can also be labels or object, # wont work.
local function count_files(files)
local ret = 0
for _ in pairs(files) do
ret = ret + 1
end
return ret
end
-- When there is multiple files, we need to wait
-- until are of them are read.
local function gen_file_transaction(self)
if count_files(self.files) == 0 then return nil end
local ret = {
counter = count_files(self.files),
files = gtable.clone(self.files, false),
filter = self.filter,
labels = {},
content = {},
failed = {},
pending = {},
}
local function finished(file, content)
assert(not ret.content[file])
self:emit_signal("file::acquired", file, content)
ret.pending[file] = nil
ret.counter = ret.counter - 1
ret.content[file] = content
if ret.counter > 0 then return end
self:emit_signal("files::acquired", ret.content, ret.failed)
-- Make the final table using the stored keys.
local contents = {}
for path, ctn in pairs(ret.content) do
if self.strip_newline then
ctn = remove_posix_extra_newline(ctn)
end
contents[self._private.file_keys[path]] = ctn
if self.labels_as_properties and type(ret.labels[path]) == "string" then
local val
if ret.filter then
val = ret.filter(ret.labels[path], ctn)
else
val = ctn
end
-- Make sure the signals are not fired for nothing.
if val ~= self[ret.labels[path]] then
self[ret.labels[path]] = val
end
end
end
local ctn = count_files(ret.files) == 1 and contents[next(contents)] or contents
if ret.filter and not self.labels_as_properties then
self._private.value = ret.filter(ctn, ret.failed)
else
self._private.value = ctn
end
self:emit_signal("property::value", self._private.value)
remove_transaction(self, ret)
end
local function read_error(file)
ret.pending[file] = nil
table.insert(ret.failed, file)
self:emit_signal("file::failed", file)
end
for label, file in pairs(ret.files) do
ret.labels[file] = label
local cancel = module._read_async(file, finished, read_error)
ret.pending[file] = cancel
end
return ret
end
local modes_start, modes_abort = {}, {}
modes_start["none"] = function() --[[nop]] end
modes_abort["none"] = function() --[[nop]] end
modes_start["files"] = function(self)
if not self._private.init then return end
local t = gen_file_transaction(self)
add_transaction(self, t)
end
modes_abort["files"] = function(self)
for _, t in ipairs(self._private.transactions) do
remove_transaction(self, t)
end
end
modes_start["command"] = function(self)
if not self._private.init then return end
local com = self._private.command
if self._private.shell then
assert(
type(com) == "string",
"When using `gears.watcher` with `shell = true`, "..
"the command must be a string"
)
com = {module._shell, '-c', com}
end
module._easy_async(com, function(lines, stderr, err, errcode)
if self.strip_newline then
lines = remove_posix_extra_newline(lines)
end
if self.filter then
self._private.value = self.filter(lines, stderr, err, errcode)
else
self._private.value = lines
end
end)
end
modes_abort["command"] = function()
--TODO
end
function module:_set_declarative_handler(parent, key, ids)
assert(type(key) == "string", "A watcher can only be attached to properties")
table.insert(self._private.targets, {
ids = ids,
parent = parent,
property = key
})
if self._private.value then
parent[key] = self._private.value
end
end
--- Abort the current transactions.
--
-- If files are currently being read or commands executed,
-- abort it. This does prevent new transaction from being
-- started. Use `:stop()` for that.
--
-- @method abort
-- @see stop
function module:abort()
modes_abort[self._private.mode](self)
end
--- Emitted when a file is read.
--
-- @signal file::acquired
-- @tparam string path The path.
-- @tparam string content The file content.
-- @see files::acquired
-- @see file::failed
--- When reading a file failed.
-- @signal file::failed
-- @tparam string path The path.
--- Emitted when all files are read.
--
-- @signal files::acquired
-- @tparam table contents Path as keys and content as values.
-- @tparam table failed The list of files which could not be read.
--- A file path.
--
-- @DOC_text_gears_watcher_simple_EXAMPLE@
--
-- @property file
-- @tparam string file
-- @propemits true false
-- @see files
--- A list or map of files.
--
-- It is often necessary to query multiple files. When reading from `proc`,
-- some data is split across multiple files, such as the battery charge.
--
-- This property accepts 2 format. One uses is a plain table of paths while
-- the other is a label->path map. Depending on what is being read, both make
-- sense.
--
-- **Simple path list:**
--
-- @DOC_text_gears_watcher_files1_EXAMPLE@
--
-- **With labels:**
--
-- @DOC_text_gears_watcher_files2_EXAMPLE@
--
-- @property files
-- @tparam table files
-- @propemits true false
-- @see labels_as_properties
function module:get_file()
return self._private.files[1]
end
function module:set_file(path)
self:set_files({path})
end
function module:get_files()
return self._private.files
end
function module:set_files(paths)
self:abort()
self._private.files = paths or {}
self._private.mode = "files"
self:emit_signal("property::files", self._private.files )
self:emit_signal("property::file" , select(2, next(self._private.files)))
-- It is possible to give names to each files. For modules like
-- battery widgets, which require reading multiple long paths from
-- `proc`, it makes the user code more readable.
self._private.file_keys = {}
for k, v in pairs(self._private.files) do
self._private.file_keys[v] = type(k) == "number" and v or k
end
modes_start["files"](self)
end
--- Add a file to the files list.
--
-- @method append_file
-- @tparam string path The path.
-- @tparam[opt] string The key.
function module:append_file(path, key)
self:abort()
if self._private.files[path] then return end
key = key or (#self._private.files + 1)
self._private.mode = "files"
self._private.files[key] = path
self._private.file_keys[path] = key
self:emit_signal("property::files", self._private.files )
self:emit_signal("property::file" , select(2, next(self._private.files)))
modes_start["files"](self)
end
--- Remove a file to the files list.
--
-- @method remove_file
-- @tparam string path The path or the key.
--- A filter to post-process the file or command.
--
-- It can be used, for example, to convert the string to a number or turn
-- the various file content into the final value. The filter argument
-- depend on various `gears.watcher` properties (as documented below).
--
-- **The callback parameters for a single file:**
-- (1) The file content (string)
--
-- **The callback parameters for a multiple files (paths):**
-- (1) Tables with the paths as keys and string content as value.
--
-- **The callback parameters for a multiple files (labels):**
-- (1) Tables with the keys as keys and string content as value.
--
-- **The callback when `labels_as_properties` is true:
-- (1) The label name
-- (2) The content
--
-- **The callback parameters for a command:**
-- (1) Stdout as first parameter
-- (2) Stderr as second parameter
-- (3) Exitreason
-- (4) Exitcode
--
-- @property filter
-- @tparam function filter
-- @propemits true false
function module:get_filter()
return self._private.filter
end
function module:set_filter(filter)
self:abort()
self._private.filter = filter
self:emit_signal("property::filter", filter)
modes_start[self._private.mode](self)
end
--- A command.
--
-- If you plan to use pipes or any shell features, do not
-- forget to also set `shell` to `true`.
--
-- @DOC_text_gears_watcher_command1_EXAMPLE@
--
-- @property command
-- @tparam table|string command
-- @propemits true false
function module:get_command()
return self._private.command
end
function module:set_command(command)
self._private.command = command
self._private.mode = "command"
self:emit_signal("property::command")
modes_start["command"](self)
end
--- Use a shell when calling the command.
--
-- This means you can use `|`, `&&`, `$?` in your command.
--
-- @DOC_text_gears_watcher_command2_EXAMPLE@
---
-- @property shell
-- @tparam[opt=false] boolean|string shell
-- @propemits true false
function module:get_shell()
return self._private.shell or false
end
function module:set_shell(shell)
self._private.shell = shell
self:emit_signal("property::shell")
end
--- In files mode, when paths have labels, apply them as properties.
--
-- @DOC_wibox_widget_declarative_watcher_EXAMPLE@
--
-- @property labels_as_properties
-- @tparam[opt=false] boolean labels_as_properties
-- @see files
--- The interval between the content refresh.
--
-- (in seconds)
--
-- @property interval
-- @tparam number interval
-- @see gears.timer.timeout
-- @propemits true false
-- There is not get_timeout/set_timeout, so we can't make aliases.
module.get_interval = gtimer.get_timeout
module.set_interval = gtimer.set_timeout
--- The current value of the watcher.
--
-- If there is no filter, this will be a string. If a filter is used,
-- then it is whatever it returns.
--
-- @property value
-- @propemits false false
function module:get_value()
return self._private.value
end
--- Strip the extra trailing newline.
--
-- All posix compliant text file and commands end with a newline.
-- Most of the time, this is inconvinient, so `gears.watcher` removes
-- them by default. Set this to `false` if this isn't the desired
-- behavior.
--
-- @property strip_newline
-- @tparam[opt=true] boolean strip_newline
-- @propemits true false
function module:get_strip_newline()
return self._private.newline
end
function module:set_strip_newline(value)
self._private.newline = value
self:emit_signal("property::strip_newline", value)
end
--- Start the timer.
-- @method start
-- @emits start
-- @baseclass gears.timer
-- @see stop
--- Stop the timer.
-- @method stop
-- @emits stop
-- @baseclass gears.timer
-- @see abort
-- @see start
--- The timer is started.
-- @property started
-- @tparam boolean started
-- @propemits false false
-- @baseclass gears.timer
--- Restart the timer.
-- This is equivalent to stopping the timer if it is running and then starting
-- it.
-- @method again
-- @baseclass gears.timer
-- @emits start
-- @emits stop
--- Create a new `gears.watcher` object.
--
-- @constructorfct gears.watcher
-- @tparam table args
-- @tparam string args.file A file path.
-- @tparam table args.files A list or map of files.
-- @tparam function args.filter A filter to post-process the file or command.
-- @tparam table|string args.command A command (without a shell).
-- @tparam[opt=false] boolean args.shell Use a shell when calling the command.
-- @param args.initial_value The value to use before the first "real" value is acquired.
-- @tparam number args.interval The interval between the content refresh.
-- @tparam boolean args.labels_as_properties Set the file labels as properties.
-- @tparam boolean args.started The timer is started.
local function new(_, args)
local ret = gtimer()
ret.timeout = 5000
local newargs = gtable.clone(args or {}, false)
ret._private.mode = "none"
ret._private.transactions = {}
ret._private.targets = {}
ret._private.files = {}
if newargs.autostart == nil then
newargs.autostart = true
end
if newargs.strip_newline == nil then
newargs.strip_newline = true
end
gtable.crush(ret, module , true )
gtable.crush(ret, newargs, false)
ret.shell = args.shell
-- ret:set_shell(args.shell)
local function value_callback()
for _, target in ipairs(ret._private.targets) do
target.parent[target.property] = ret.value
end
end
local function update_callback()
modes_start[ret._private.mode](ret)
end
ret:connect_signal("property::value", value_callback)
ret:connect_signal("timeout", update_callback)
if args.initial_value then
ret._private.value = args.initial_value
end
ret._private.init = true
if newargs.autostart then
ret:start()
modes_start[ret._private.mode](ret)
end
return ret
end
--@DOC_object_COMMON@
return setmetatable(module, {__call = new})

View File

@ -70,15 +70,12 @@ end
-- @beautiful beautiful.menubar_bg_focus
-- @param color
--- Menubar font.
-- @beautiful beautiful.menubar_font
-- @param[opt=beautiful.font] font
-- menubar
local menubar = { menu_entries = {} }
menubar.menu_gen = require("menubar.menu_gen")
menubar.utils = require("menubar.utils")
local compute_text_width = menubar.utils.compute_text_width
-- Options section
@ -122,9 +119,10 @@ menubar.right_label = "▶▶"
-- @tfield[opt="◀◀"] string left_label
menubar.left_label = "◀◀"
-- awful.widget.common.list_update adds spacing of dpi(4) between items.
-- @tfield number list_spacing
local list_spacing = theme.xresources.apply_dpi(4)
-- awful.widget.common.list_update adds three times a margin of dpi(4)
-- for each item:
-- @tfield number list_interspace
local list_interspace = theme.xresources.apply_dpi(4) * 3
--- Allows user to specify custom parameters for prompt.run function
-- (like colors). This will merge with the default parameters, overriding affected values.
@ -151,7 +149,7 @@ end
--- Get how the menu item should be displayed.
-- @param o The menu item.
-- @return item name, item background color, background image, item icon, item args.
-- @return item name, item background color, background image, item icon.
local function label(o)
local fg_color = theme.menubar_fg_normal or theme.menu_fg_normal or theme.fg_normal
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
@ -162,8 +160,7 @@ local function label(o)
return colortext(gstring.xml_escape(o.name), fg_color),
bg_color,
nil,
o.icon,
o.icon and {icon_size=instance.geometry.height}
o.icon
end
local function load_count_table()
@ -228,11 +225,6 @@ end
-- @tparam number|screen scr Screen
-- @return table List of items for current page.
local function get_current_page(all_items, query, scr)
local compute_text_width = function(text, s)
return wibox.widget.textbox.get_markup_geometry(text, s, instance.font)['width']
end
scr = get_screen(scr)
if not instance.prompt.width then
instance.prompt.width = compute_text_width(instance.prompt.prompt, scr)
@ -243,19 +235,16 @@ local function get_current_page(all_items, query, scr)
if not menubar.right_label_width then
menubar.right_label_width = compute_text_width(menubar.right_label, scr)
end
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
local available_space = instance.geometry.width - menubar.right_margin -
menubar.right_label_width - menubar.left_label_width -
compute_text_width(query..' ', scr) - instance.prompt.width - border_width * 2
-- space character is added as input cursor placeholder
compute_text_width(query, scr) - instance.prompt.width
local width_sum = 0
local current_page = {}
for i, item in ipairs(all_items) do
item.width = item.width or (
compute_text_width(label(item), scr) +
(item.icon and (instance.geometry.height + list_spacing) or 0) + list_spacing * 2
)
item.width = item.width or
compute_text_width(item.name, scr) +
(item.icon and instance.geometry.height or 0) + list_interspace
if width_sum + item.width > available_space then
if current_item < i then
table.insert(current_page, { name = menubar.right_label, icon = nil })
@ -460,7 +449,6 @@ function menubar.show(scr)
local bg_color = theme.menubar_bg_normal or theme.menu_bg_normal or theme.bg_normal
local border_width = theme.menubar_border_width or theme.menu_border_width or 0
local border_color = theme.menubar_border_color or theme.menu_border_color
local font = theme.menubar_font or theme.font or "Monospace 10"
if not instance then
-- Add to each category the name of its key in all_categories
@ -479,13 +467,11 @@ function menubar.show(scr)
fg = fg_color,
border_width = border_width,
border_color = border_color,
font = font,
},
widget = common_args.w,
prompt = awful.widget.prompt(),
query = nil,
count_table = nil,
font = font,
}
local layout = wibox.layout.fixed.horizontal()
layout:add(instance.prompt)
@ -504,7 +490,7 @@ function menubar.show(scr)
local geometry = menubar.geometry
instance.geometry = {x = geometry.x or scrgeom.x,
y = geometry.y or scrgeom.y,
height = geometry.height or gmath.round(theme.get_font_height(font) * 1.5),
height = geometry.height or gmath.round(theme.get_font_height() * 1.5),
width = (geometry.width or scrgeom.width) - border_width * 2}
instance.wibox:geometry(instance.geometry)

View File

@ -19,6 +19,7 @@ local glib = lgi.GLib
local w_textbox = require("wibox.widget.textbox")
local gdebug = require("gears.debug")
local protected_call = require("gears.protected_call")
local gstring = require("gears.string")
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local utils = {}
@ -409,8 +410,6 @@ function utils.parse_dir(dir_path, callback)
end)
end
-- luacov: disable
function utils.compute_textbox_width(textbox, s)
gdebug.deprecate("Use 'width, _ = textbox:get_preferred_size(s)' directly.", {deprecated_in=4})
s = screen[s or mouse.screen]
@ -418,13 +417,16 @@ function utils.compute_textbox_width(textbox, s)
return w
end
function utils.compute_text_width(text, s, font)
gdebug.deprecate("Use 'width = textbox.get_markup_geometry(text, s, font)['width']'.", {deprecated_in=4})
return w_textbox.get_markup_geometry(text, s, font)['width']
--- Compute text width.
-- @tparam str text Text.
-- @tparam number|screen s Screen
-- @treturn int Text width.
-- @staticfct menubar.utils.compute_text_width
function utils.compute_text_width(text, s)
local w, _ = w_textbox(gstring.xml_escape(text)):get_preferred_size(s)
return w
end
-- luacov: enable
return utils
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -57,13 +57,7 @@ local action = {}
-- @propemits true false
--- When a notification is invoked.
--
-- Note that it is possible to call `:invoke()` without a notification object.
-- It is possible the `notification` parameter will be nil.
--
-- @signal invoked
-- @tparam naughty.action action The action.
-- @tparam naughty.notification|nil notification The notification, if known.
function action:get_selected()
return self._private.selected

View File

@ -191,13 +191,7 @@ naughty.notifications = { suspended = { }, _expired = {{}} }
naughty._active = {}
local function get_screen(s)
return s and capi.screen[s]
end
local function init_screen(s)
if naughty.notifications[s] then return end
screen.connect_for_each_screen(function(s)
naughty.notifications[s] = {
top_left = {},
top_middle = {},
@ -207,9 +201,7 @@ local function init_screen(s)
bottom_right = {},
middle = {},
}
end
screen.connect_for_each_screen(init_screen)
end)
capi.screen.connect_signal("removed", function(scr)
-- Allow the notifications to be moved to another screen.
@ -222,18 +214,15 @@ capi.screen.connect_signal("removed", function(scr)
naughty.emit_signal("request::screen", n, "removed", {})
end
end
for _, n in ipairs(naughty._active) do
if n._private.args and get_screen(n._private.args.screen) == scr then
n._private.args.screen = nil
end
end
-- Destroy all notifications on this screen
naughty.destroy_all_notifications({scr})
naughty.notifications[scr] = nil
end)
local function get_screen(s)
return s and capi.screen[s]
end
local function remove_from_index(n)
for _, positions in pairs(naughty.notifications) do
for _, ns in pairs(positions) do
@ -657,10 +646,6 @@ local function register(notification, args)
assert(s)
if not naughty.notifications[s] then
init_screen(get_screen(s))
end
-- insert the notification to the table
table.insert(naughty._active, notification)
table.insert(naughty.notifications[s][notification.position], notification)

View File

@ -8,7 +8,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2017 Emmanuel Lepage Vallee
-- @popupmod naughty.layout.box
-- @supermodule awful.popup
----------------------------------------------------------------------------
local capi = {screen=screen}

View File

@ -62,12 +62,6 @@ screen.connect_for_each_screen(function(s)
}
end)
capi.screen.connect_signal("removed", function(s)
timer.delayed_call(function()
current_notifications[s] = nil
end)
end)
--- Sum heights of notifications at position
--
-- @param s Screen to use
@ -134,7 +128,7 @@ local function get_offset(s, position, idx, width, height)
local find_old_to_replace = function()
for i = 1, idx-1 do
local n = current_notifications[s][position][i]
if n and n.timeout > 0 then
if n.timeout > 0 then
return n
end
end

View File

@ -337,10 +337,7 @@ local function new(_, args)
update_style(wdg)
wdg._private.default_buttons = gtable.join(
abutton({ }, 1, function(a)
local notif = wdg._private.notification or args.notification
a:invoke(notif)
end)
abutton({ }, 1, function(a) a:invoke(args.notification) end)
)
return wdg

View File

@ -570,27 +570,6 @@ function notification:set_timeout(timeout)
self:emit_signal("property::timeout", timeout)
end
function notification:get_message()
-- This property was called "text" in older versions.
-- Some modules like `lain` abused of the presets (they
-- had little choice at the time) to set the message on
-- an existing popup.
local p = rawget(self, "preset") or {}
local message = self._private.message or p.message or ""
if message == "" and p.text and p.text ~= "" then
gdebug.deprecate(
"Using the preset configuration to set the notification "..
"message is not supported. Please use `n.message = 'text'`.",
{deprecated_in=5}
)
return p.text
end
return message
end
function notification:set_text(txt)
gdebug.deprecate(
"The `text` attribute is deprecated, use `message`",
@ -882,18 +861,10 @@ local function select_legacy_preset(n, args)
))
for k, v in pairs(n.preset) do
-- Don't keep a strong reference to the screen, Lua 5.1 GC wont be
-- smart enough to unwind the mess of circular weak references.
if k ~= "screen" then
n._private[k] = v
end
end
if n.preset.screen then
n._private.weak_screen[1] = capi.screen[n.preset.screen]
end
end
--- Create a notification.
--
-- @tparam table args The argument table containing any of the arguments below.

View File

@ -160,15 +160,6 @@ end
-- @staticfct ruled.notification.apply
function module.apply(n)
local callbacks, props = {}, {}
if n.preset then
for k, v in pairs(n.preset) do
if not n._private[v] then
props[k] = v
end
end
end
for _, v in ipairs(nrules._matching_source) do
v.callback(nrules, n, props, callbacks)
end

View File

@ -8,7 +8,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2013 Emmanuel Lepage Vallee
-- @containermod wibox.container.arcchart
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -358,6 +357,10 @@ function arcchart.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(arcchart, arcchart.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -6,7 +6,6 @@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @containermod wibox.container.background
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local base = require("wibox.widget.base")
@ -485,6 +484,10 @@ function background.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(background, background.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -5,7 +5,6 @@
-- @author Lukáš Hrázký
-- @copyright 2012 Lukáš Hrázký
-- @containermod wibox.container.constraint
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -177,6 +176,10 @@ function constraint.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(constraint, constraint.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -17,7 +17,6 @@ return setmetatable({
radialprogressbar = require("wibox.container.radialprogressbar");
arcchart = require("wibox.container.arcchart");
place = require("wibox.container.place");
tile = require("wibox.container.tile");
}, {__call = function(_, args) return base.make_widget_declarative(args) end})
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -5,7 +5,6 @@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @containermod wibox.container.margin
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local pairs = pairs
@ -239,6 +238,10 @@ function margin.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(margin, margin.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -5,7 +5,6 @@
-- @author dodo
-- @copyright 2012 dodo
-- @containermod wibox.container.mirror
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local type = type
@ -130,6 +129,10 @@ function mirror.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(mirror, mirror.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -5,7 +5,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2016 Emmanuel Lepage Vallee
-- @containermod wibox.container.place
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -23,8 +22,13 @@ local align_fct = {
}
align_fct.top, align_fct.bottom = align_fct.left, align_fct.right
-- Shared with some subclasses like the `tiled` and `scaled` modules.
function place:_layout(context, width, height)
-- Layout this layout
function place:layout(context, width, height)
if not self._private.widget then
return
end
local w, h = base.fit_widget(self, context, self._private.widget, width, height)
if self._private.content_fill_horizontal then
@ -40,21 +44,6 @@ function place:_layout(context, width, height)
local x, y = align_fct[halign](w, width), align_fct[valign](h, height)
-- Sub pixels makes everything blurry. This is now what people expect.
x, y = math.floor(x), math.floor(y)
return x, y, w, h
end
-- Layout this layout
function place:layout(context, width, height)
if not self._private.widget then
return
end
local x, y, w, h = self:_layout(context, width, height)
return { base.place_widget_at(self._private.widget, x, y, w, h) }
end
@ -107,8 +96,6 @@ end
-- * *center* (default)
-- * *bottom*
--
--@DOC_wibox_container_place_valign_EXAMPLE@
--
-- @property valign
-- @tparam[opt="center"] string valign
-- @propemits true false
@ -121,8 +108,6 @@ end
-- * *center* (default)
-- * *right*
--
--@DOC_wibox_container_place_halign_EXAMPLE@
--
-- @property halign
-- @tparam[opt="center"] string halign
-- @propemits true false
@ -173,8 +158,6 @@ end
--- Stretch the contained widget so it takes all the vertical space.
--
--@DOC_wibox_container_place_content_fill_vertical_EXAMPLE@
--
-- @property content_fill_vertical
-- @tparam[opt=false] boolean content_fill_vertical
-- @propemits true false
@ -187,8 +170,6 @@ end
--- Stretch the contained widget so it takes all the horizontal space.
--
--@DOC_wibox_container_place_content_fill_horizontal_EXAMPLE@
--
-- @property content_fill_horizontal
-- @tparam[opt=false] boolean content_fill_horizontal
-- @propemits true false
@ -222,6 +203,10 @@ function place.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(place, place.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -8,7 +8,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2013 Emmanuel Lepage Vallee
-- @containermod wibox.container.radialprogressbar
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -282,6 +281,10 @@ function radialprogressbar.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(radialprogressbar, radialprogressbar.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -5,7 +5,6 @@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @containermod wibox.container.rotate
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local error = error
@ -150,6 +149,10 @@ function rotate.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(rotate, rotate.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -21,7 +21,6 @@
-- @author Uli Schlachter (based on ideas from Saleur Geoffrey)
-- @copyright 2015 Uli Schlachter
-- @containermod wibox.container.scroll
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local cache = require("gears.cache")
@ -550,6 +549,10 @@ function scroll.step_functions.waiting_nonlinear_back_and_forth(elapsed, size, v
return (size - visible_size) * state
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return scroll
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,212 +0,0 @@
---------------------------------------------------------------------------
-- Replicate the content of the widget over and over.
--
-- This contained is intended to be used for wallpapers. It currently doesn't
-- support mouse input in the replicated tiles.
--
--@DOC_wibox_container_defaults_tile_EXAMPLE@
-- @author Emmanuel Lepage-Vallee
-- @copyright 2021 Emmanuel Lepage-Vallee
-- @containermod wibox.container.tile
-- @supermodule wibox.container.place
local place = require("wibox.container.place")
local cairo = require("lgi").cairo
local widget = require("wibox.widget")
local gtable = require("gears.table")
local module = {mt = {}}
function module:draw(context, cr, width, height)
if not self._private.tiled then return end
if not self._private.widget then return end
local x, y, w, h = self:_layout(context, width, height)
local vspace, hspace = self.vertical_spacing, self.horizontal_spacing
local vcrop, hcrop = self.vertical_crop, self.horizontal_crop
-- In theory we could avoid a few repaints by tracking the child widget
-- redraw independently from the container redraw. However it is nearly a
-- 1:1 march, so there's little reasons to do it.
if not self._private.surface then
self._private.surface = cairo.ImageSurface(cairo.Format.ARGB32, w+hspace, h+vspace)
self._private.cr = cairo.Context(self._private.surface)
self._private.cr:set_source(cr:get_source())
self._private.pattern = cairo.Pattern.create_for_surface(self._private.surface)
self._private.pattern.extend = cairo.Extend.REPEAT
self._private.cr:translate(math.ceil(hspace), math.ceil(vspace))
else
self._private.cr:set_operator(cairo.Operator.CLEAR)
self._private.cr:set_source_rgba(0,0,0,1)
self._private.cr:paint()
self._private.cr:set_operator(cairo.Operator.SOURCE)
end
widget.draw_to_cairo_context(self._private.widget, self._private.cr, w, h, context)
cr:save()
-- We do our own clip.
cr:reset_clip()
local x0, y0 = 0, 0
-- Avoid painting incomplete tiles
if hcrop and x ~= 0 then
x0 = x - math.floor(x/(w+hspace))*(w+hspace)
end
if hcrop then
width = x + w + hspace + math.floor((width - (x + w + hspace))/(w+hspace))*(w+hspace)
end
if vcrop and y ~= 0 then
y0 = y - math.floor(y/(h+vspace))*(h+vspace)
end
if vcrop then
height = (y+h+vspace) + math.floor((height - (y+h+vspace))/(h+vspace))*(h+vspace)
end
-- Create a clip around the "real" widget in case there is some transparency.
cr:rectangle(x0, y0, width-x0, y-y0)
cr:rectangle(x0, y0, x-hspace-x0, height-y0)
cr:rectangle(x+hspace+w, y0, width - (x+w+hspace), height-y0)
cr:rectangle(x, y+vspace+h, w+hspace, height - (y+h+vspace))
cr:clip()
-- Make sure the tiles are aligned with the child widget.
cr:translate(x - hspace, y - vspace)
-- Use OVER rather than SOURCE to preserve the alpha.
cr.operator = cairo.Operator.OVER
cr.source = self._private.pattern
cr:paint()
cr:restore()
end
--- The horizontal spacing between the tiled.
--
--@DOC_wibox_container_tile_horizontal_spacing_EXAMPLE@
--
-- @property horizontal_spacing
-- @tparam number horizontal_spacing
-- @propemits true false
-- @see vertical_spacing
--- The vertical spacing between the tiled.
--
--@DOC_wibox_container_tile_vertical_spacing_EXAMPLE@
--
-- @property vertical_spacing
-- @tparam number vertical_spacing
-- @propemits true false
-- @see horizontal_spacing
--- Avoid painting incomplete horizontal tiles.
--
--@DOC_wibox_container_tile_horizontal_crop_EXAMPLE@
--
-- @property horizontal_crop
-- @tparam[opt=false] boolean tiled
-- @see vertical_crop
--- Avoid painting incomplete vertical tiles.
--
--@DOC_wibox_container_tile_vertical_crop_EXAMPLE@
--
-- @property vertical_crop
-- @tparam[opt=false] boolean tiled
-- @see horizontal_crop
--- Enable or disable the tiling.
--
-- When set to `false`, this container behaves exactly like
-- `wibox.container.place`.
--
--@DOC_wibox_container_tile_tiled_EXAMPLE@
--
-- @property tiled
-- @tparam[opt=true] boolean tiled
local defaults = {
horizontal_spacing = 0,
vertical_spacing = 0,
tiled = true,
horizontal_crop = false,
vertical_crop = false,
}
for prop in pairs(defaults) do
module["set_"..prop] = function(self, value)
self._private[prop] = value
self:emit_signal("widget::redraw_needed", value)
end
module["get_"..prop] = function(self)
if self._private[prop] == nil then
return defaults[prop]
end
return self._private[prop]
end
end
local function new(_, args)
args = args or {}
local ret = place(args.widget, args.halign, args.valign)
gtable.crush(ret, module, true)
ret._private.tiled = true
local function redraw()
ret:emit_signal("widget::redraw_needed")
end
-- Resize the pattern as needed.
local function reset()
if ret._private.surface then
ret._private.surface:finish()
end
ret._private.cr = nil
ret._private.surface = nil
ret._private.pattern = nil
end
local w = nil
ret:connect_signal("property::widget", function()
reset()
if w then
w:disconnect_signal("widget::redraw_needed", redraw)
w:disconnect_signal("widget::layout_changed", reset)
end
w = ret._private.widget
if w then
w:connect_signal("widget::redraw_needed", redraw)
w:connect_signal("widget::layout_changed", reset)
end
end)
return ret
end
--- Create a new tile container.
-- @tparam table args
-- @tparam wibox.widget widget args.widget The widget to tile.
-- @tparam string args.halign Either `left`, `right` or `center`.
-- @tparam string args.valign Either `top`, `bottom` or `center`.
-- @constructorfct wibox.container.tile
function module.mt:__call(...)
return new(...)
end
return setmetatable(module, module.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,29 +1,9 @@
---------------------------------------------------------------------------
-- The `align` layout has three slots for child widgets. On its main axis, it
-- will use as much space as is available to it and distribute that to its child
-- widgets by stretching or shrinking them based on the chosen @{expand}
-- strategy.
-- On its secondary axis, the biggest child widget determines the size of the
-- layout, but smaller widgets will not be stretched to match it.
--
-- In its default configuration, the layout will give the first and third
-- widgets only the minimum space they ask for and it aligns them to the outer
-- edges. The remaining space between them is made available to the widget in
-- slot two.
--
-- This layout is most commonly used to split content into left/top, center and
-- right/bottom sections. As such, it is usually seen as the root layout in
-- @{awful.wibar}.
--
-- You may also fill just one or two of the widget slots, the @{expand} algorithm
-- will adjust accordingly.
--
--@DOC_wibox_layout_defaults_align_EXAMPLE@
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @layoutmod wibox.layout.align
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local table = table
@ -159,10 +139,8 @@ function align:layout(context, width, height)
return result
end
--- The widget in slot one.
--
-- This is the widget that is at the left/top.
--
--- Set the layout's first widget.
-- This is the widget that is at the left/top
-- @property first
-- @tparam widget first
-- @propemits true false
@ -176,10 +154,7 @@ function align:set_first(widget)
self:emit_signal("property::first", widget)
end
--- The widget in slot two.
--
-- This is the centered one.
--
--- Set the layout's second widget. This is the centered one.
-- @property second
-- @tparam widget second
-- @propemits true false
@ -193,10 +168,8 @@ function align:set_second(widget)
self:emit_signal("property::second", widget)
end
--- The widget in slot three.
--
-- This is the widget that is at the right/bottom.
--
--- Set the layout's third widget.
-- This is the widget that is at the right/bottom
-- @property third
-- @tparam widget third
-- @propemits true false
@ -253,27 +226,23 @@ function align:fit(context, orig_width, orig_height)
return used_in_dir, used_in_other
end
--- Set the expand mode, which determines how child widgets expand to take up
--- Set the expand mode which determines how sub widgets expand to take up
-- unused space.
--
-- The following values are valid:
--
-- * `"inside"`: The widgets in slot one and three are set to their minimal
-- required size. The widget in slot two is then given the remaining space.
-- This is the default behaviour.
-- * `"outside"`: The widget in slot two is set to its minimal required size and
-- placed in the center of the space available to the layout. The other
-- widgets are then given the remaining space on either side.
-- If the center widget requires all available space, the outer widgets are
-- not drawn at all.
-- * `"none"`: All widgets are given their minimal required size or the
-- remaining space, whichever is smaller. The center widget gets priority.
--
-- Attempting to set any other value than one of those three will fall back to
-- `"inside"`.
-- * "inside" - Default option. Size of outside widgets is determined using
-- their fit function. Second, middle, or center widget expands to fill
-- remaining space.
-- * "outside" - Center widget is sized using its fit function and placed in
-- the center of the allowed space. Outside widgets expand (or contract) to
-- fill remaining space on their side.
-- * "none" - All widgets are sized using their fit function, drawn to only the
-- returned space, or remaining space, whichever is smaller. Center widget
-- gets priority.
--
-- @property expand
-- @tparam[opt="inside"] string mode How to use unused space.
-- @tparam[opt=inside] string mode How to use unused space.
function align:set_expand(mode)
if mode == "none" or mode == "outside" then
@ -313,16 +282,14 @@ local function get_layout(dir, first, second, third)
return ret
end
--- Returns a new horizontal align layout.
--
-- The three widget slots are aligned left, center and right.
--
-- Additionally, this creates the aliases `set_left`, `set_middle` and
-- `set_right` to assign @{first}, @{second} and @{third} respectively.
--- Returns a new horizontal align layout. An align layout can display up to
-- three widgets. The widget set via :set_left() is left-aligned. :set_right()
-- sets a widget which will be right-aligned. The remaining space between those
-- two will be given to the widget set via :set_middle().
-- @constructorfct wibox.layout.align.horizontal
-- @tparam[opt] widget left Widget to be put in slot one.
-- @tparam[opt] widget middle Widget to be put in slot two.
-- @tparam[opt] widget right Widget to be put in slot three.
-- @tparam[opt] widget left Widget to be put to the left.
-- @tparam[opt] widget middle Widget to be put to the middle.
-- @tparam[opt] widget right Widget to be put to the right.
function align.horizontal(left, middle, right)
local ret = get_layout("x", left, middle, right)
@ -333,16 +300,14 @@ function align.horizontal(left, middle, right)
return ret
end
--- Returns a new vertical align layout.
--
-- The three widget slots are aligned top, center and bottom.
--
-- Additionally, this creates the aliases `set_top`, `set_middle` and
-- `set_bottom` to assign @{first}, @{second} and @{third} respectively.
--- Returns a new vertical align layout. An align layout can display up to
-- three widgets. The widget set via :set_top() is top-aligned. :set_bottom()
-- sets a widget which will be bottom-aligned. The remaining space between those
-- two will be given to the widget set via :set_middle().
-- @constructorfct wibox.layout.align.vertical
-- @tparam[opt] widget top Widget to be put in slot one.
-- @tparam[opt] widget middle Widget to be put in slot two.
-- @tparam[opt] widget bottom Widget to be put in slot three.
-- @tparam[opt] widget top Widget to be put to the top.
-- @tparam[opt] widget middle Widget to be put to the middle.
-- @tparam[opt] widget bottom Widget to be put to the right.
function align.vertical(top, middle, bottom)
local ret = get_layout("y", top, middle, bottom)
@ -355,6 +320,10 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return align
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,26 +1,9 @@
---------------------------------------------------------------------------
-- A `fixed` layout may be initialized with any number of child widgets, and
-- during runtime widgets may be added and removed dynamically.
--
-- On the main axis, child widgets are given a fixed size of exactly as much
-- space as they ask for. The layout will then resize according to the sum of
-- all child widgets. If the space available to the layout is not enough to
-- include all child widgets, the excessive ones are not drawn at all.
--
-- Additionally, the layout allows adding empty spacing or even placing a custom
-- spacing widget between the child widget.
--
-- On its secondary axis, the layout's size is determined by the largest child
-- widget. Smaller child widgets are then placed with the same size.
-- Therefore, child widgets may ignore their `forced_width` or `forced_height`
-- properties for vertical and horizontal layouts respectively.
--
--@DOC_wibox_layout_defaults_fixed_EXAMPLE@
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @layoutmod wibox.layout.fixed
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
@ -37,96 +20,45 @@ local fixed = {}
-- @param height The available height.
function fixed:layout(context, width, height)
local result = {}
local spacing = self._private.spacing or 0
local pos,spacing = 0, self._private.spacing
local spacing_widget = self._private.spacing_widget
local is_y = self._private.dir == "y"
local is_x = not is_y
local abspace = math.abs(spacing)
local spoffset = spacing < 0 and 0 or spacing
local widgets_nr = #self._private.widgets
local spacing_widget
local x, y = 0, 0
spacing_widget = spacing ~= 0 and self._private.spacing_widget or nil
for index, widget in pairs(self._private.widgets) do
local w, h, local_spacing = width - x, height - y, spacing
-- Some widget might be zero sized either because this is their
-- minimum space or just because they are really empty. In this case,
-- they must still be added to the layout. Otherwise, if their size
-- change and this layout is resizable, they are lost "forever" until
-- a full relayout is called on this fixed layout object.
local zero = false
for k, v in pairs(self._private.widgets) do
local x, y, w, h, _
if is_y then
if index ~= widgets_nr or not self._private.fill_space then
h = select(2, base.fit_widget(self, context, widget, w, h))
zero = h == 0
end
if y - spacing >= height then
-- pop the spacing widget added in previous iteration if used
if spacing_widget then
table.remove(result)
-- Avoid adding zero-sized widgets at an out-of-bound
-- position.
y = y - spacing
end
-- Never display "random" widgets as soon as a non-zero sized
-- one doesn't fit.
if not zero then
break
end
x, y = 0, pos
w, h = width, height - pos
if k ~= #self._private.widgets or not self._private.fill_space then
_, h = base.fit_widget(self, context, v, w, h);
end
pos = pos + h + spacing
else
if index ~= widgets_nr or not self._private.fill_space then
w = select(1, base.fit_widget(self, context, widget, w, h))
zero = w == 0
x, y = pos, 0
w, h = width - pos, height
if k ~= #self._private.widgets or not self._private.fill_space then
w, _ = base.fit_widget(self, context, v, w, h);
end
pos = pos + w + spacing
end
if x - spacing >= width then
-- pop the spacing widget added in previous iteration if used
if spacing_widget then
table.remove(result)
-- Avoid adding zero-sized widgets at an out-of-bound
-- position.
x = x - spacing
end
-- Never display "random" widgets as soon as a non-zero sized
-- one doesn't fit.
if not zero then
if (is_y and pos-spacing > height) or
(is_x and pos-spacing > width) then
break
end
end
end
if zero then
local_spacing = 0
end
-- Place widget, even if it has zero width/height. Otherwise
-- any layout change for zero-sized widget would become invisible.
table.insert(result, base.place_widget_at(widget, x, y, w, h))
x = is_x and x + w + local_spacing or x
y = is_y and y + h + local_spacing or y
-- Add the spacing widget (if needed)
if index < widgets_nr and spacing_widget then
-- Add the spacing widget
if k > 1 and abspace > 0 and spacing_widget then
table.insert(result, base.place_widget_at(
spacing_widget,
is_x and (x - spoffset) or x,
is_y and (y - spoffset) or y,
is_x and abspace or w,
is_y and abspace or h
spacing_widget, is_x and (x - spoffset) or x, is_y and (y - spoffset) or y,
is_x and abspace or w, is_y and abspace or h
))
end
table.insert(result, base.place_widget_at(v, x, y, w, h))
end
return result
end
@ -285,13 +217,9 @@ function fixed:set(index, widget2)
return true
end
--- A widget to insert as a separator between child widgets.
--- The widget used to fill the spacing between the layout elements.
--
-- If this property is a valid widget and `spacing` is greater than `0`, a
-- copy of this widget is inserted between each child widget, with its size in
-- the layout's main direction determined by `spacing`.
--
-- By default no widget is used and any `spacing` is applied as an empty offset.
-- By default, no widget is used.
--
--@DOC_wibox_layout_fixed_spacing_widget_EXAMPLE@
--
@ -333,61 +261,40 @@ end
-- @param orig_width The available width.
-- @param orig_height The available height.
function fixed:fit(context, orig_width, orig_height)
local width_left, height_left = orig_width, orig_height
local spacing = self._private.spacing or 0
local widgets_nr = #self._private.widgets
local is_y = self._private.dir == "y"
local used_max = 0
local width, height = orig_width, orig_height
local used_in_dir, used_max = 0, 0
-- when no widgets exist the function can be called with orig_width or
-- orig_height equal to nil. Exit early in this case.
if widgets_nr == 0 then
return 0, 0
end
for k, v in pairs(self._private.widgets) do
local w, h = base.fit_widget(self, context, v, width_left, height_left)
local max
if is_y then
max = w
height_left = height_left - h
for _, v in pairs(self._private.widgets) do
local w, h = base.fit_widget(self, context, v, width, height)
local in_dir, max
if self._private.dir == "y" then
max, in_dir = w, h
height = height - in_dir
else
max = h
width_left = width_left - w
in_dir, max = w, h
width = width - in_dir
end
if max > used_max then
used_max = max
end
used_in_dir = used_in_dir + in_dir
if k < widgets_nr then
if is_y then
height_left = height_left - spacing
if width <= 0 or height <= 0 then
if self._private.dir == "y" then
used_in_dir = orig_height
else
width_left = width_left - spacing
end
end
if width_left <= 0 or height_left <= 0 then
-- this complicated two lines determine whether we're out-of-space
-- because of spacing, or if the last widget doesn't fit in
if is_y then
height_left = k < widgets_nr and height_left + spacing or height_left
height_left = height_left < 0 and 0 or height_left
else
width_left = k < widgets_nr and width_left + spacing or width_left
width_left = width_left < 0 and 0 or width_left
used_in_dir = orig_width
end
break
end
end
if is_y then
return used_max, orig_height - height_left
end
local spacing = self._private.spacing * (#self._private.widgets-1)
return orig_width - width_left, used_max
if self._private.dir == "y" then
return used_max, used_in_dir + spacing
end
return used_in_dir + spacing, used_max
end
function fixed:reset()
@ -429,25 +336,29 @@ local function get_layout(dir, widget1, ...)
return ret
end
--- Creates and returns a new horizontal fixed layout.
--
--- Returns a new horizontal fixed layout. Each widget will get as much space as it
-- asks for and each widget will be drawn next to its neighboring widget.
-- Widgets can be added via :add() or as arguments to this function.
-- Note that widgets ignore `forced_height`. They will use the preferred/minimum width
-- on the horizontal axis, and a stretched height on the vertical axis.
-- @tparam widget ... Widgets that should be added to the layout.
-- @constructorfct wibox.layout.fixed.horizontal
function fixed.horizontal(...)
return get_layout("x", ...)
end
--- Creates and returns a new vertical fixed layout.
--
--- Returns a new vertical fixed layout. Each widget will get as much space as it
-- asks for and each widget will be drawn next to its neighboring widget.
-- Widgets can be added via :add() or as arguments to this function.
-- Note that widgets ignore `forced_width`. They will use the preferred/minimum height
-- on the vertical axis, and a stretched width on the horizontal axis.
-- @tparam widget ... Widgets that should be added to the layout.
-- @constructorfct wibox.layout.fixed.vertical
function fixed.vertical(...)
return get_layout("y", ...)
end
--- The amount of space inserted between the child widgets.
--
-- If a `spacing_widget` is defined, this value is used for its size.
--- Add spacing between each layout widgets.
--
--@DOC_wibox_layout_fixed_spacing_EXAMPLE@
--
@ -470,6 +381,10 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return fixed
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,25 +1,9 @@
---------------------------------------------------------------------------
-- A `flex` layout may be initialized with any number of child widgets, and
-- during runtime widgets may be added and removed dynamically.
--
-- On the main axis, the layout will divide the available space evenly between
-- all child widgets, without any regard to how much space these widgets might
-- be asking for.
--
-- Just like @{wibox.layout.fixed}, `flex` allows adding spacing between the
-- widgets, either as an ofset via @{spacing} or with a
-- @{spacing_widget}.
--
-- On its secondary axis, the layout's size is determined by the largest child
-- widget. Smaller child widgets are then placed with the same size.
-- Therefore, child widgets may ignore their `forced_width` or `forced_height`
-- properties for vertical and horizontal layouts respectively.
--
--@DOC_wibox_layout_defaults_flex_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @layoutmod wibox.layout.flex
-- @supermodule wibox.layout.fixed
---------------------------------------------------------------------------
local base = require("wibox.widget.base")
@ -31,16 +15,6 @@ local gtable = require("gears.table")
local flex = {}
-- {{{ Override inherited properties we want to hide
--- From `wibox.layout.fixed`.
-- @property fill_space
-- @tparam boolean fill_space
-- @propemits true false
-- @hidden
-- }}}
--- Add some widgets to the given fixed layout.
--
-- @tparam widget ... Widgets that should be added (must at least be one).
@ -71,18 +45,14 @@ local flex = {}
-- @treturn boolean If the operation is successful
-- @method insert
-- @emits widget::inserted
-- @emitstparam widget::inserted widget self The layout.
-- @emitstparam widget::inserted widget widget The inserted widget.
-- @emitstparam widget::inserted widget self The fixed layout.
-- @emitstparam widget::inserted widget widget index The inserted widget.
-- @emitstparam widget::inserted number count The widget count.
-- @interface layout
--- A widget to insert as a separator between child widgets.
--- The widget used to fill the spacing between the layout elements.
--
-- If this property is a valid widget and @{spacing} is greater than `0`, a
-- copy of this widget is inserted between each child widget, with its size in
-- the layout's main direction determined by @{spacing}.
--
-- By default no widget is used and any @{spacing} is applied as an empty offset.
-- By default, no widget is used.
--
--@DOC_wibox_layout_flex_spacing_widget_EXAMPLE@
--
@ -91,16 +61,13 @@ local flex = {}
-- @propemits true false
-- @interface layout
--- The amount of space inserted between the child widgets.
--
-- If a @{spacing_widget} is defined, this value is used for its size.
--- Add spacing between each layout widgets.
--
--@DOC_wibox_layout_flex_spacing_EXAMPLE@
--
-- @property spacing
-- @tparam number spacing Spacing between widgets.
-- @propemits true false
-- @interface layout
function flex:layout(_, width, height)
local result = {}
@ -217,7 +184,10 @@ local function get_layout(dir, widget1, ...)
return ret
end
--- Creates and returns a new horizontal flex layout.
--- Returns a new horizontal flex layout.
--
-- A flex layout shares the available space.
-- equally among all widgets. Widgets can be added via `:add(widget)`.
--
-- @tparam widget ... Widgets that should be added to the layout.
-- @constructorfct wibox.layout.flex.horizontal
@ -225,7 +195,10 @@ function flex.horizontal(...)
return get_layout("horizontal", ...)
end
--- Creates and returns a new vertical flex layout.
--- Returns a new vertical flex layout.
--
-- A flex layout shares the available space
-- equally among all widgets. Widgets can be added via `:add(widget)`.
--
-- @tparam widget ... Widgets that should be added to the layout.
-- @constructorfct wibox.layout.flex.vertical
@ -235,6 +208,10 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return flex
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -14,7 +14,6 @@
-- @author getzze
-- @copyright 2017 getzze
-- @layoutmod wibox.layout.grid
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -962,6 +961,10 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(grid, grid.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -8,7 +8,6 @@
-- @author Emmanuel Lepage Vallee
-- @copyright 2016 Emmanuel Lepage Vallee
-- @layoutmod wibox.layout.manual
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local gtable = require("gears.table")
local base = require("wibox.widget.base")
@ -246,4 +245,8 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(manual_layout, {__call=function(_,...) return new_manual(...) end})

View File

@ -7,8 +7,6 @@
-- @author Emmanuel Lepage Vallee
-- @copyright 2016 Emmanuel Lepage Vallee
-- @layoutmod wibox.layout.ratio
-- @supermodule wibox.layout.flex
-- @see 03-declarative-layout.md
---------------------------------------------------------------------------
local base = require("wibox.widget.base" )
@ -526,6 +524,10 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return ratio
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -12,7 +12,6 @@
-- @author Emmanuel Lepage Vallee
-- @copyright 2016 Emmanuel Lepage Vallee
-- @layoutmod wibox.layout.stack
-- @supermodule wibox.layout.fixed
---------------------------------------------------------------------------
local base = require("wibox.widget.base" )
@ -211,5 +210,9 @@ end
--@DOC_fixed_COMMON@
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(stack, stack.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -2,7 +2,6 @@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @classmod wibox.widget.base
-- @supermodule gears.object
---------------------------------------------------------------------------
local object = require("gears.object")
@ -18,184 +17,6 @@ local table = table
local base = {}
-- {{{ Properties available on all widgets
--- Get or set the children elements.
-- @property children
-- @tparam table children The children.
-- @baseclass wibox.widget.base
--- Get all direct and indirect children widgets.
-- This will scan all containers recursively to find widgets
-- Warning: This method it prone to stack overflow if there is a loop in the
-- widgets hierarchy. A hierarchy loop is when a widget, or any of its
-- children, contain (directly or indirectly) itself.
-- @property all_children
-- @tparam table children The children.
-- @baseclass wibox.widget.base
--- Force a widget height.
-- @property forced_height
-- @tparam number|nil height The height (`nil` for automatic)
-- @baseclass wibox.widget.base
--- Force a widget width.
-- @property forced_width
-- @tparam number|nil width The width (`nil` for automatic)
-- @baseclass wibox.widget.base
--- The widget opacity (transparency).
-- @property opacity
-- @tparam[opt=1] number opacity The opacity (between 0 and 1)
-- @baseclass wibox.widget.base
--- The widget visibility.
-- @property visible
-- @param boolean
-- @baseclass wibox.widget.base
--- The widget buttons.
--
-- The table contains a list of `awful.button` objects.
-- @property buttons
-- @param table
-- @see awful.button
-- @baseclass wibox.widget.base
-- }}}
-- {{{ Signals available on the widgets.
--- When the layout (size) change.
-- This signal is emitted when the previous results of `:layout()` and `:fit()`
-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()`
-- must return the same result when called with the same arguments.
-- @signal widget::layout_changed
-- @see widget::redraw_needed
-- @baseclass wibox.widget.base
--- When the widget content changed.
-- This signal is emitted when the content of the widget changes. The widget will
-- be redrawn, it is not re-layouted. Put differently, it is assumed that
-- `:layout()` and `:fit()` would still return the same results as before.
-- @signal widget::redraw_needed
-- @see widget::layout_changed
-- @baseclass wibox.widget.base
--- When a mouse button is pressed over the widget.
-- @signal button::press
-- @tparam table self The current object instance itself.
-- @tparam number lx The horizontal position relative to the (0,0) position in
-- the widget.
-- @tparam number ly The vertical position relative to the (0,0) position in the
-- widget.
-- @tparam number button The button number.
-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift)
-- @tparam table find_widgets_result The entry from the result of
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
-- the widget.
-- @tparam widget find_widgets_result.widget The widget being displayed.
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
-- managing the widget's geometry.
-- @tparam number find_widgets_result.x An approximation of the X position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.y An approximation of the Y position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.width An approximation of the width that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.height An approximation of the height that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.widget_width The exact width of the widget
-- in its local coordinate system.
-- @tparam number find_widgets_result.widget_height The exact height of the widget
-- in its local coordinate system.
-- @see mouse
-- @baseclass wibox.widget.base
--- When a mouse button is released over the widget.
-- @signal button::release
-- @tparam table self The current object instance itself.
-- @tparam number lx The horizontal position relative to the (0,0) position in
-- the widget.
-- @tparam number ly The vertical position relative to the (0,0) position in the
-- widget.
-- @tparam number button The button number.
-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift)
-- @tparam table find_widgets_result The entry from the result of
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
-- the widget.
-- @tparam widget find_widgets_result.widget The widget being displayed.
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
-- managing the widget's geometry.
-- @tparam number find_widgets_result.x An approximation of the X position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.y An approximation of the Y position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.width An approximation of the width that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.height An approximation of the height that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.widget_width The exact width of the widget
-- in its local coordinate system.
-- @tparam number find_widgets_result.widget_height The exact height of the widget
-- in its local coordinate system.
-- @see mouse
-- @baseclass wibox.widget.base
--- When the mouse enter a widget.
-- @signal mouse::enter
-- @tparam table self The current object instance itself.
-- @tparam table find_widgets_result The entry from the result of
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
-- the widget.
-- @tparam widget find_widgets_result.widget The widget being displayed.
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
-- managing the widget's geometry.
-- @tparam number find_widgets_result.x An approximation of the X position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.y An approximation of the Y position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.width An approximation of the width that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.height An approximation of the height that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.widget_width The exact width of the widget
-- in its local coordinate system.
-- @tparam number find_widgets_result.widget_height The exact height of the widget
-- in its local coordinate system.
-- @see mouse
-- @baseclass wibox.widget.base
--- When the mouse leave a widget.
-- @signal mouse::leave
-- @tparam table self The current object instance itself.
-- @tparam table find_widgets_result The entry from the result of
-- @{wibox.drawable:find_widgets} for the position that the mouse hit.
-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing
-- the widget.
-- @tparam widget find_widgets_result.widget The widget being displayed.
-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy
-- managing the widget's geometry.
-- @tparam number find_widgets_result.x An approximation of the X position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.y An approximation of the Y position that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.width An approximation of the width that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.height An approximation of the height that
-- the widget is visible at on the surface.
-- @tparam number find_widgets_result.widget_width The exact width of the widget
-- in its local coordinate system.
-- @tparam number find_widgets_result.widget_height The exact height of the widget
-- in its local coordinate system.
-- @see mouse
-- @baseclass wibox.widget.base
-- }}}
-- {{{ Functions on widgets
-- Functions available on all widgets.
@ -209,8 +30,7 @@ end, true)
--- Set a widget's visibility.
-- @tparam boolean b Whether the widget is visible.
-- @method wibox.widget.base:set_visible
-- @hidden
-- @method set_visible
function base.widget:set_visible(b)
if b ~= self._private.visible then
self._private.visible = b
@ -222,7 +42,6 @@ end
--- Add a new `awful.button` to this widget.
-- @tparam awful.button button The button to add.
-- @method wibox.widget.base:add_button
function base.widget:add_button(button)
if not button then return end
@ -246,8 +65,7 @@ end
--- Is the widget visible?
-- @treturn boolean
-- @method wibox.widget.base:get_visible
-- @hidden
-- @method get_visible
function base.widget:get_visible()
return self._private.visible or false
end
@ -255,19 +73,17 @@ end
--- Set a widget's opacity.
-- @tparam number o The opacity to use (a number from 0 (transparent) to 1
-- (opaque)).
-- @method wibox.widget.base:set_opacity
-- @hidden
-- @method set_opacity
function base.widget:set_opacity(o)
if o ~= self._private.opacity then
self._private.opacity = o
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::redraw")
end
end
--- Get the widget's opacity.
-- @treturn number The opacity (between 0 (transparent) and 1 (opaque)).
-- @method wibox.widget.base:get_opacity
-- @hidden
-- @method get_opacity
function base.widget:get_opacity()
return self._private.opacity
end
@ -275,9 +91,8 @@ end
--- Set the widget's forced width.
-- @tparam[opt] number width With `nil` the default mechanism of calling the
-- `:fit` method is used.
-- @see wibox.widget.base:fit_widget
-- @method wibox.widget.base:set_forced_width
-- @hidden
-- @see fit_widget
-- @method set_forced_width
function base.widget:set_forced_width(width)
if width ~= self._private.forced_width then
self._private.forced_width = width
@ -293,8 +108,7 @@ end
-- actual size is during a `mouse::enter`, `mouse::leave` or button event.
-- @treturn[opt] number The forced width (nil if automatic).
-- @see fit_widget
-- @method wibox.widget.base:get_forced_width
-- @hidden
-- @method get_forced_width
function base.widget:get_forced_width()
return self._private.forced_width
end
@ -302,9 +116,8 @@ end
--- Set the widget's forced height.
-- @tparam[opt] number height With `nil` the default mechanism of calling the
-- `:fit` method is used.
-- @see wibox.widget.base:fit_widget
-- @method wibox.widget.base:set_height
-- @hidden
-- @see fit_widget
-- @method set_height
function base.widget:set_forced_height(height)
if height ~= self._private.forced_height then
self._private.forced_height = height
@ -319,8 +132,7 @@ end
-- If there is no forced width/height, then the only way to get the widget's
-- actual size is during a `mouse::enter`, `mouse::leave` or button event.
-- @treturn[opt] number The forced height (nil if automatic).
-- @method wibox.widget.base:get_forced_height
-- @hidden
-- @method get_forced_height
function base.widget:get_forced_height()
return self._private.forced_height
end
@ -329,8 +141,7 @@ end
--
-- This method should be re-implemented by the relevant widgets.
-- @treturn table children The children.
-- @method wibox.widget.base:get_children
-- @hidden
-- @method get_children
function base.widget:get_children()
return {}
end
@ -340,8 +151,7 @@ end
-- The default implementation does nothing, this must be re-implemented by
-- all layout and container widgets.
-- @tparam table children A table composed of valid widgets.
-- @method wibox.widget.base:set_children
-- @hidden
-- @method set_children
function base.widget:set_children(children) -- luacheck: no unused
-- Nothing on purpose
end
@ -361,8 +171,7 @@ end
-- *Warning*: This method it prone to stack overflow if the widget, or any of
-- its children, contains (directly or indirectly) itself.
-- @treturn table children The children.
-- @method wibox.widget.base:get_all_children
-- @hidden
-- @method get_all_children
function base.widget:get_all_children()
local ret = {}
digg_children(ret, self)
@ -384,17 +193,12 @@ function base.set_widget_common(self, widget)
end
self._private.widget = w
self:emit_signal("property::widget")
self:emit_signal("widget::layout_changed")
end
--- Emit a signal and ensure all parent widgets in the hierarchies also
-- forward the signal.
--
-- This is useful to track signals when there is a dynamic set of containers
-- and layouts wrapping the widget.
-- forward the signal. This is useful to track signals when there is a dynamic
-- set of containers and layouts wrapping the widget.
--
-- Note that this function has some flaws:
--
@ -409,7 +213,7 @@ end
--
-- @tparam string signal_name
-- @param ... Other arguments
-- @method wibox.widget.base:emit_signal_recursive
-- @method emit_signal_recursive
function base.widget:emit_signal_recursive(signal_name, ...)
-- This is a convenience wrapper, the real implementation is in the
-- hierarchy.
@ -419,14 +223,12 @@ end
--- Get the index of a widget.
-- @tparam widget widget The widget to look for.
-- @tparam[opt] boolean recursive Recursively check accross the sub-widgets
-- hierarchy.
-- @tparam[opt] widget ... Additional widgets to add at the end of the
-- sub-widgets hierarchy "path".
-- @treturn number The widget index.
-- @tparam[opt] boolean recursive Also check sub-widgets?
-- @tparam[opt] widget ... Additional widgets to add at the end of the "path"
-- @treturn number The index.
-- @treturn widget The parent widget.
-- @treturn table The hierarchy path between "self" and "widget".
-- @method wibox.widget.base:index
-- @treturn table The path between "self" and "widget".
-- @method index
function base.widget:index(widget, recursive, ...)
local widgets = self:get_children()
for idx, w in ipairs(widgets) do
@ -669,7 +471,8 @@ end
-- Read the table, separate attributes from widgets.
local function parse_table(t, leave_empty)
local max = 0
local attributes, widgets = {}, {}
local attributes, widgets, with_handlers = {}, {}, {}
for k,v in pairs(t) do
if type(k) == "number" then
if v then
@ -679,20 +482,35 @@ local function parse_table(t, leave_empty)
max = k
end
-- Those are not added "normally". They have their own callback
-- to add them.
if rawget(v, "_set_declarative_handler") then
with_handlers[k] = v
else
widgets[k] = v
end
end
elseif type(v) == "table" and rawget(v, "_set_declarative_handler") then
with_handlers[k] = v
else
attributes[k] = v
end
end
-- Make sure there is no holes in the widgets table.
if #with_handlers > 0 then
for k in pairs(with_handlers) do
table.remove(widgets, k)
end
end
-- Pack the sparse table, if the container doesn't support sparse tables.
if not leave_empty then
widgets = gtable.from_sparse(widgets)
max = #widgets
end
return max, attributes, widgets
return max, attributes, widgets, with_handlers
end
-- Recursively build a container from a declarative table.
@ -708,8 +526,19 @@ local function drill(ids, content)
-- Create layouts based on metatable's __call.
local l = layout.is_widget and layout or layout()
-- Check if the widget added it's own element by ids
--FIXME #2181
if l and l._private and l._private.by_id then
for k, v in pairs(l._private.by_id) do
ids[k] = ids[k] or {}
for _, v2 in ipairs(v) do
table.insert(ids[k], v2)
end
end
end
-- Get the number of children widgets (including nil widgets).
local max, attributes, widgets = parse_table(content, l.allow_empty_widget)
local max, attributes, widgets, with_handlers = parse_table(content, l.allow_empty_widget)
-- Get the optional identifier to create a virtual widget tree to place
-- in an "access table" to be able to retrieve the widget.
@ -731,7 +560,14 @@ local function drill(ids, content)
end
end
if widgets and max > 0 then
-- Let some containers handle the template themselves.
-- This can be used for use cases such as lazy loading, repeaters or
-- conditional loaders.
if l._accept_templates and l.set_templates then
l:set_templates(widgets)
return l, id
end
-- Add all widgets.
for k = 1, max do
-- ipairs cannot be used on sparse tables.
@ -756,9 +592,16 @@ local function drill(ids, content)
end
-- Replace all children (if any) with the new ones.
if widgets then
l:set_children(widgets)
end
-- Now that all the children are set, call the custom handlers.
-- The order is undefined.
for k, v in pairs(with_handlers) do
v:_set_declarative_handler(l, k, ids)
end
return l, id
end
@ -775,8 +618,7 @@ end
--
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html).
-- @tparam table args A table containing the widget's disposition.
-- @method wibox.widget.base:setup
-- @hidden
-- @method setup
function base.widget:setup(args)
local f,ids = self.set_widget or self.add or self.set_first,{}
local w, id = drill(ids, args)

View File

@ -37,9 +37,8 @@ local beautiful = require("beautiful")
local calendar = { mt = {} }
local properties = { "date" , "font" , "spacing" , "week_numbers",
"start_sunday", "long_weekdays", "fn_embed", "flex_height",
}
local properties = { "date", "font", "spacing", "week_numbers", "start_sunday", "long_weekdays", "fn_embed" }
--- The calendar font.
-- @beautiful beautiful.calendar_font
@ -61,11 +60,6 @@ local properties = { "date" , "font" , "spacing" , "week_numbers"
-- @beautiful beautiful.calendar_long_weekdays
-- @param boolean Use three characters for the weekdays instead of two
--- Allow cells to have flexible height.
-- Flexible height allow cells to adapt their height to fill the empty space at the bottom of the widget.
-- @beautiful beautiful.flex_height
-- @param boolean Cells can skretch to fill the empty space.
--- The calendar date.
--
-- A table representing the date {day=[number|nil], month=[number|nil], year=[number]}.
@ -122,12 +116,6 @@ local properties = { "date" , "font" , "spacing" , "week_numbers"
-- @param function Function to embed the widget depending on its flag
-- @property fn_embed
--- Allow cells to have flexible height
--
--@DOC_wibox_widget_calendar_flex_height_EXAMPLE@
--
-- @param[opt=false] boolean Allow flex height.
-- @property flex_height
--- Make a textbox
-- @tparam string text Text of the textbox
@ -151,47 +139,25 @@ end
-- @tparam number|nil date.day Date day
-- @treturn widget Grid layout
local function create_month(props, date)
local start_row = 3
local week_start = props.start_sunday and 1 or 2
local last_day = os.date("*t", os.time{year=date.year, month=date.month+1, day=0})
local month_days = last_day.day
local column_fday = (last_day.wday - month_days + 1 - week_start ) % 7
local num_rows = 8
local num_columns = props.week_numbers and 8 or 7
local start_column = num_columns - 6
-- Compute number of rows
-- There are at least 4 weeks in a month
local num_rows = 4
-- On every month but february on non bisextile years
if last_day.day > 28 then
-- The number of days span over at least 5 weeks
num_rows = num_rows + 1
-- On month with 30+ days add 1 week if:
-- - if 30 days and the first day is the last day of the week
-- - if 31 days and the first days is at least the second to last day
if column_fday >= 5 then
if last_day.day == 30 and column_fday == 6 or last_day.day == 31 then
num_rows = num_rows + 1
end
end
-- If the first day of february is anything but the first day of the week
elseif column_fday > 1 then
-- Span over 5 weeks
num_rows = num_rows + 1
end
-- Create grid layout
local layout = grid()
if props.flex_height then
layout:set_expand(true)
end
layout:set_expand(true)
layout:set_homogeneous(true)
layout:set_spacing(props.spacing)
layout:set_forced_num_rows(num_rows)
layout:set_forced_num_cols(num_columns)
local start_row = 3
local start_column = num_columns - 6
local week_start = props.start_sunday and 1 or 2
local last_day = os.date("*t", os.time{year=date.year, month=date.month+1, day=0})
local month_days = last_day.day
local column_fday = (last_day.wday - month_days + 1 - week_start ) % 7
--local flags = {"header", "weekdays", "weeknumber", "normal", "focus"}
local cell_date, t, i, j, w, flag, text
@ -365,7 +331,6 @@ local function get_calendar(type, date, font)
ret._private.week_numbers = beautiful.calendar_week_numbers or false
ret._private.start_sunday = beautiful.calendar_start_sunday or false
ret._private.long_weekdays = beautiful.calendar_long_weekdays or false
ret._private.flex_height = beautiful.calendar_flex_height or false
ret._private.fn_embed = function (w, _) return w end
-- header specific

View File

@ -9,7 +9,6 @@
-- @author Emmanuel Lepage Valle
-- @copyright 2010 Emmanuel Lepage Vallee
-- @widgetmod wibox.widget.checkbox
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local color = require( "gears.color" )
@ -304,6 +303,10 @@ local function new(checked, args)
return ret
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable({}, { __call = function(_, ...) return new(...) end})
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,26 @@
---------------------------------------------------------------------------
-- A widget to display an image.
-- A widget to display image.
--
-- The `wibox.widget.imagebox` is part of the Awesome WM's widget system
-- The `wibox.widget.imagebox` is part of the Awesome WM's wiboxes system
-- (see @{03-declarative-layout.md}).
--
-- This widget displays an image. The image can be a file,
-- a cairo image surface, or an rsvg handle object (see the
-- [image property](#image)).
--
-- Examples using a `wibox.widget.imagebox`:
-- Use a `wibox.widget.imagebox`
-- ---
--
-- @DOC_wibox_widget_defaults_imagebox_EXAMPLE@
--
-- Alternatively, you can declare the `imagebox` widget using the
-- declarative pattern (both variants are strictly equivalent):
-- declarative pattern (Both codes are strictly equivalent):
--
-- @DOC_wibox_widget_declarative-pattern_imagebox_EXAMPLE@
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @widgetmod wibox.widget.imagebox
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local lgi = require("lgi")
@ -48,40 +47,67 @@ end
local imagebox = { mt = {} }
local rsvg_handle_cache = setmetatable({}, { __mode = 'k' })
local rsvg_handle_cache = setmetatable({}, { __mode = 'v' })
---Load rsvg handle form image file
-- @tparam string file Path to svg file.
-- @return Rsvg handle
-- @treturn table A table where cached data can be stored.
---@tparam string file Path to svg file.
---@return Rsvg handle
local function load_rsvg_handle(file)
if not Rsvg then return end
local cache = (rsvg_handle_cache[file] or {})["handle"]
local cache = rsvg_handle_cache[file]
if cache then
return cache, rsvg_handle_cache[file]
end
local handle, err
if file:match("<[?]?xml") or file:match("<svg") then
handle, err = Rsvg.Handle.new_from_data(file)
else
handle, err = Rsvg.Handle.new_from_file(file)
return cache
end
local handle, err = Rsvg.Handle.new_from_file(file)
if not err then
rsvg_handle_cache[file] = rsvg_handle_cache[file] or {}
rsvg_handle_cache[file]["handle"] = handle
return handle, rsvg_handle_cache[file]
rsvg_handle_cache[file] = handle
return handle
end
end
-- Draw an imagebox with the given cairo context in the given geometry.
function imagebox:draw(_, cr, width, height)
if width == 0 or height == 0 or not self._private.default then return end
-- Set the clip
if self._private.clip_shape then
cr:clip(self._private.clip_shape(cr, width, height, unpack(self._private.clip_args)))
end
if not self._private.resize_forbidden then
-- Let's scale the image so that it fits into (width, height)
local w, h = self._private.default.width, self._private.default.height
local aspect = math.min(width / w, height / h)
cr:scale(aspect, aspect)
end
if self._private.handle then
self._private.handle:render_cairo(cr)
else
cr:set_source_surface(self._private.image, 0, 0)
cr:paint()
end
end
-- Fit the imagebox into the given geometry
function imagebox:fit(_, width, height)
if not self._private.default then return 0, 0 end
local w, h = self._private.default.width, self._private.default.height
if not self._private.resize_forbidden or w > width or h > height then
local aspect = math.min(width / w, height / h)
return w * aspect, h * aspect
end
return w, h
end
---Apply cairo surface for given imagebox widget
local function set_surface(ib, surf)
local is_surf_valid = surf.width > 0 and surf.height > 0
if not is_surf_valid then return false end
local is_surd_valid = surf.width > 0 and surf.height > 0
if not is_surd_valid then return end
ib._private.default = { width = surf.width, height = surf.height }
ib._private.handle = nil
@ -90,16 +116,14 @@ local function set_surface(ib, surf)
end
---Apply RsvgHandle for given imagebox widget
local function set_handle(ib, handle, cache)
local function set_handle(ib, handle)
local dim = handle:get_dimensions()
local is_handle_valid = dim.width > 0 and dim.height > 0
if not is_handle_valid then return false end
if not is_handle_valid then return end
ib._private.default = { width = dim.width, height = dim.height }
ib._private.handle = handle
ib._private.cache = cache
ib._private.image = nil
return true
end
@ -111,208 +135,32 @@ end
---@treturn boolean True if image was successfully applied
local function load_and_apply(ib, file, image_loader, image_setter)
local image_applied
local object, cache = image_loader(file)
local object = image_loader(file)
if object then
image_applied = image_setter(ib, object, cache)
image_applied = image_setter(ib, object)
end
return image_applied
end
---Update the cached size depending on the stylesheet and dpi.
--
-- It's necessary because a single RSVG handle can be used by
-- many imageboxes. So DPI and Stylesheet need to be set each time.
local function update_dpi(self, ctx)
if not self._private.handle then return end
local dpi = self._private.auto_dpi and
ctx.dpi or
self._private.dpi or
nil
local need_dpi = dpi and
self._private.last_dpi ~= dpi
local need_style = self._private.handle.set_stylesheet and
self._private.stylesheet
local old_size = self._private.default and self._private.default.width
if dpi and dpi ~= self._private.cache.dpi then
if type(dpi) == "table" then
self._private.handle:set_dpi_x_y(dpi.x, dpi.y)
else
self._private.handle:set_dpi(dpi)
end
end
if need_style and self._private.cache.stylesheet ~= self._private.stylesheet then
self._private.handle:set_stylesheet(self._private.stylesheet)
end
-- Reload the size.
if need_dpi or (need_style and self._private.stylesheet ~= self._private.last_stylesheet) then
set_handle(self, self._private.handle, self._private.cache)
end
self._private.last_dpi = dpi
self._private.cache.dpi = dpi
self._private.last_stylesheet = self._private.stylesheet
self._private.cache.stylesheet = self._private.stylesheet
-- This can happen in the constructor when `dpi` is set after `image`.
if old_size and old_size ~= self._private.default.width then
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
end
-- Draw an imagebox with the given cairo context in the given geometry.
function imagebox:draw(ctx, cr, width, height)
if width == 0 or height == 0 or not self._private.default then return end
-- For valign = "top" and halign = "left"
local translate = {
x = 0,
y = 0,
}
update_dpi(self, ctx)
local w, h = self._private.default.width, self._private.default.height
if self._private.resize then
-- That's for the "fit" policy.
local aspects = {
w = width / w,
h = height / h
}
local policy = {
w = self._private.horizontal_fit_policy or "auto",
h = self._private.vertical_fit_policy or "auto"
}
for _, aspect in ipairs {"w", "h"} do
if self._private.upscale == false and (w < width and h < height) then
aspects[aspect] = 1
elseif self._private.downscale == false and (w >= width and h >= height) then
aspects[aspect] = 1
elseif policy[aspect] == "none" then
aspects[aspect] = 1
elseif policy[aspect] == "auto" then
aspects[aspect] = math.min(width / w, height / h)
end
end
if self._private.halign == "center" then
translate.x = math.floor((width - w*aspects.w)/2)
elseif self._private.halign == "right" then
translate.x = math.floor(width - (w*aspects.w))
end
if self._private.valign == "center" then
translate.y = math.floor((height - h*aspects.h)/2)
elseif self._private.valign == "bottom" then
translate.y = math.floor(height - (h*aspects.h))
end
cr:translate(translate.x, translate.y)
-- Before using the scale, make sure it is below the threshold.
local threshold, max_factor = self._private.max_scaling_factor, math.max(aspects.w, aspects.h)
if threshold and threshold > 0 and threshold < max_factor then
aspects.w = (aspects.w*threshold)/max_factor
aspects.h = (aspects.h*threshold)/max_factor
end
-- Set the clip
if self._private.clip_shape then
cr:clip(self._private.clip_shape(cr, w*aspects.w, h*aspects.h, unpack(self._private.clip_args)))
end
cr:scale(aspects.w, aspects.h)
else
if self._private.halign == "center" then
translate.x = math.floor((width - w)/2)
elseif self._private.halign == "right" then
translate.x = math.floor(width - w)
end
if self._private.valign == "center" then
translate.y = math.floor((height - h)/2)
elseif self._private.valign == "bottom" then
translate.y = math.floor(height - h)
end
cr:translate(translate.x, translate.y)
-- Set the clip
if self._private.clip_shape then
cr:clip(self._private.clip_shape(cr, w, h, unpack(self._private.clip_args)))
end
end
if self._private.handle then
self._private.handle:render_cairo(cr)
else
cr:set_source_surface(self._private.image, 0, 0)
local filter = self._private.scaling_quality
if filter then
cr:get_source():set_filter(cairo.Filter[filter:upper()])
end
cr:paint()
end
end
-- Fit the imagebox into the given geometry
function imagebox:fit(ctx, width, height)
if not self._private.default then return 0, 0 end
update_dpi(self, ctx)
local w, h = self._private.default.width, self._private.default.height
if w <= width and h <= height and self._private.upscale == false then
return w, h
end
if (w < width or h < height) and self._private.downscale == false then
return w, h
end
if self._private.resize or w > width or h > height then
local aspect = math.min(width / w, height / h)
return w * aspect, h * aspect
end
return w, h
end
--- The image rendered by the `imagebox`.
--
-- It can can be any of the following:
--
-- * A `string`: Interpreted as a path to an image file
-- * A cairo image surface: Directly used as-is
-- * A librsvg handle object: Directly used as-is
-- * A `string` : Interpreted as the path to an image file,
-- * A cairo image surface : Directly used as is,
-- * An rsvg handle object : Directly used as is,
-- * `nil` : Unset the image.
--
-- @property image
-- @tparam image image The image to render.
-- @propemits false false
-- @see set_image
--- Set the `imagebox` image.
--
-- The image can be a file, a cairo image surface, or an rsvg handle object
-- (see the [image property](#image)).
-- @method set_image
-- @hidden
-- @method imagebox:set_image
-- @tparam image image The image to render.
-- @treturn boolean `true` on success, `false` if the image cannot be used.
-- @usage my_imagebox:set_image(beautiful.awesome_icon)
@ -321,10 +169,7 @@ end
function imagebox:set_image(image)
local setup_succeed
-- Keep the original to prevent the cache from being GCed.
self._private.original_image = image
if type(image) == "userdata" and not (Rsvg and Rsvg.Handle:is_type_of(image)) then
if type(image) == "userdata" then
-- This function is not documented to handle userdata objects, but
-- historically it did, and it did by just assuming they refer to a
-- cairo surface.
@ -341,8 +186,7 @@ function imagebox:set_image(image)
end
elseif Rsvg and Rsvg.Handle:is_type_of(image) then
-- try to apply given rsvg handle
rsvg_handle_cache[image] = rsvg_handle_cache[image] or {}
setup_succeed = set_handle(self, image, rsvg_handle_cache[image])
setup_succeed = set_handle(self, image)
elseif cairo.Surface:is_type_of(image) then
-- try to apply given cairo surface
setup_succeed = set_surface(self, image)
@ -363,27 +207,23 @@ function imagebox:set_image(image)
end
--- Set a clip shape for this imagebox.
--
-- A clip shape defines an area and dimension to which the content should be
-- trimmed.
--
-- @DOC_wibox_widget_imagebox_clip_shape_EXAMPLE@
-- A clip shape define an area where the content is displayed and one where it
-- is trimmed.
--
-- @property clip_shape
-- @tparam function|gears.shape clip_shape A `gears.shape` compatible shape function.
-- @propemits true false
-- @see gears.shape
-- @see set_clip_shape
--- Set a clip shape for this imagebox.
-- A clip shape define an area where the content is displayed and one where it
-- is trimmed.
--
-- A clip shape defines an area and dimensions to which the content should be
-- trimmed.
--
-- Additional parameters will be passed to the clip shape function.
-- Any other parameters will be passed to the clip shape function.
--
-- @tparam function|gears.shape clip_shape A `gears_shape` compatible shape function.
-- @method set_clip_shape
-- @hidden
-- @method imagebox:set_clip_shape
-- @see gears.shape
-- @see clip_shape
function imagebox:set_clip_shape(clip_shape, ...)
@ -394,282 +234,22 @@ function imagebox:set_clip_shape(clip_shape, ...)
end
--- Should the image be resized to fit into the available space?
--
-- Note that `upscale` and `downscale` can affect the value of `resize`.
-- If conflicting values are passed to the constructor, then the result
-- is undefined.
--
-- @DOC_wibox_widget_imagebox_resize_EXAMPLE@
-- @property resize
-- @propemits true false
-- @tparam boolean resize
--- Allow the image to be upscaled (made bigger).
--
-- Note that `upscale` and `downscale` can affect the value of `resize`.
-- If conflicting values are passed to the constructor, then the result
-- is undefined.
--
-- @DOC_wibox_widget_imagebox_upscale_EXAMPLE@
-- @property upscale
-- @tparam boolean upscale
-- @see downscale
-- @see resize
--- Allow the image to be downscaled (made smaller).
--
-- Note that `upscale` and `downscale` can affect the value of `resize`.
-- If conflicting values are passed to the constructor, then the result
-- is undefined.
--
-- @DOC_wibox_widget_imagebox_downscale_EXAMPLE@
-- @property downscale
-- @tparam boolean downscale
-- @see upscale
-- @see resize
--- Set the SVG CSS stylesheet.
--
-- If the image is an SVG (vector graphics), this property allows to set
-- a CSS stylesheet. It can be used to set colors and much more.
--
-- Note that this property is a string, not a path. If the stylesheet is
-- stored on disk, read the content first.
--
--@DOC_wibox_widget_imagebox_stylesheet_EXAMPLE@
--
-- @property stylesheet
-- @tparam string stylesheet
-- @propemits true false
--- Set the SVG DPI (dot per inch).
--
-- Force a specific DPI when rendering the `.svg`. For other file formats,
-- this does nothing.
--
-- It can either be a number of a table containing the `x` and `y` keys.
--
-- Please note that DPI and `resize` can "fight" each other and end up
-- making the image smaller instead of bigger.
--
--@DOC_wibox_widget_imagebox_dpi_EXAMPLE@
--
-- @property dpi
-- @tparam number|table dpi
-- @propemits true false
-- @see auto_dpi
--- Use the object DPI when rendering the SVG.
--
-- By default, the SVG are interpreted as-is. When this property is set,
-- the screen DPI will be passed to the SVG renderer. Depending on which
-- tool was used to create the `.svg`, this may do nothing at all. However,
-- for example, if the `.svg` uses `<text>` elements and doesn't have an
-- hardcoded stylesheet, the result will differ.
--
-- @property auto_dpi
-- @tparam[opt=false] boolean auto_dpi
-- @propemits true false
-- @see dpi
for _, prop in ipairs {"stylesheet", "dpi", "auto_dpi"} do
imagebox["set_" .. prop] = function(self, value)
-- It will be set in :fit and :draw. The handle is shared
-- by multiple imagebox, so it cannot be set just once.
self._private[prop] = value
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
self:emit_signal("property::" .. prop)
end
end
--- Should the image be resized to fit into the available space?
-- @tparam boolean allowed If `false`, the image will be clipped, else it will
-- be resized to fit into the available space.
-- @method imagebox:set_resize
function imagebox:set_resize(allowed)
self._private.resize = allowed
if allowed then
self._private.downscale = true
self._private.upscale = true
self:emit_signal("property::downscale", allowed)
self:emit_signal("property::upscale", allowed)
end
self._private.resize_forbidden = not allowed
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
self:emit_signal("property::resize", allowed)
end
for _, prop in ipairs {"downscale", "upscale" } do
imagebox["set_" .. prop] = function(self, allowed)
self._private[prop] = allowed
if self._private.resize ~= (self._private.upscale or self._private.downscale) then
self._private.resize = self._private.upscale or self._private.downscale
self:emit_signal("property::resize", self._private.resize)
end
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
self:emit_signal("property::"..prop, allowed)
end
end
--- Set the horizontal fit policy.
--
-- Valid values are:
--
-- * `"auto"`: Honor the `resize` variable and preserve the aspect ratio.
-- This is the default behaviour.
-- * `"none"`: Do not resize at all.
-- * `"fit"`: Resize to the widget width.
--
-- Here is the result for a 22x32 image:
--
-- @DOC_wibox_widget_imagebox_horizontal_fit_policy_EXAMPLE@
--
-- @property horizontal_fit_policy
-- @tparam[opt="auto"] string horizontal_fit_policy
-- @propemits true false
-- @see vertical_fit_policy
-- @see resize
--- Set the vertical fit policy.
--
-- Valid values are:
--
-- * `"auto"`: Honor the `resize` varible and preserve the aspect ratio.
-- This is the default behaviour.
-- * `"none"`: Do not resize at all.
-- * `"fit"`: Resize to the widget height.
--
-- Here is the result for a 32x22 image:
--
-- @DOC_wibox_widget_imagebox_vertical_fit_policy_EXAMPLE@
--
-- @property vertical_fit_policy
-- @tparam[opt="auto"] string horizontal_fit_policy
-- @propemits true false
-- @see horizontal_fit_policy
-- @see resize
--- The vertical alignment.
--
-- Possible values are:
--
-- * `"top"`
-- * `"center"` (default)
-- * `"bottom"`
--
-- @DOC_wibox_widget_imagebox_valign_EXAMPLE@
--
-- @property valign
-- @tparam[opt="center"] string valign
-- @propemits true false
-- @see wibox.container.place
-- @see halign
--- The horizontal alignment.
--
-- Possible values are:
--
-- * `"left"`
-- * `"center"` (default)
-- * `"right"`
--
-- @DOC_wibox_widget_imagebox_halign_EXAMPLE@
--
-- @property halign
-- @tparam[opt="center"] string halign
-- @propemits true false
-- @see wibox.container.place
-- @see valign
--- The maximum scaling factor.
--
-- If an image is scaled too much, it gets very blurry. This
-- property allows to limit the scaling.
-- Use the properties `valign` and `halign` to control how the image will be
-- aligned.
--
-- In the example below, the original size is 22x22
--
-- @DOC_wibox_widget_imagebox_max_scaling_factor_EXAMPLE@
--
-- @property max_scaling_factor
-- @tparam number max_scaling_factor
-- @propemits true false
-- @see valign
-- @see halign
-- @see scaling_quality
--- Set the scaling aligorithm.
--
-- Depending on how the image is used, what is the "correct" way to
-- scale can change. For example, upscaling a pixel art image should
-- not make it blurry. However, scaling up a photo should not make it
-- blocky.
--
--<table class='widget_list' border=1>
-- <tr style='font-weight: bold;'>
-- <th align='center'>Value</th>
-- <th align='center'>Description</th>
-- </tr>
-- <tr><td>fast</td><td>A high-performance filter</td></tr>
-- <tr><td>good</td><td>A reasonable-performance filter</td></tr>
-- <tr><td>best</td><td>The highest-quality available</td></tr>
-- <tr><td>nearest</td><td>Nearest-neighbor filtering (blocky)</td></tr>
-- <tr><td>bilinear</td><td>Linear interpolation in two dimensions</td></tr>
--</table>
--
-- The image used in the example below has a resolution of 32x22 and is
-- intentionally blocky to highlight the difference.
-- It is zoomed by a factor of 3.
--
-- @DOC_wibox_widget_imagebox_scaling_quality_EXAMPLE@
--
-- @property scaling_quality
-- @tparam string scaling_quality Either `"fast"`, `"good"`, `"best"`,
-- `"nearest"` or `"bilinear"`.
-- @propemits true false
-- @see resize
-- @see horizontal_fit_policy
-- @see vertical_fit_policy
-- @see max_scaling_factor
local defaults = {
halign = "left",
valign = "top",
horizontal_fit_policy = "auto",
vertical_fit_policy = "auto",
max_scaling_factor = 0,
scaling_quality = "good"
}
local function get_default(prop, value)
if value == nil then return defaults[prop] end
return value
end
for prop in pairs(defaults) do
imagebox["set_"..prop] = function(self, value)
if value == self._private[prop] then return end
self._private[prop] = get_default(prop, value)
self:emit_signal("widget::redraw_needed")
self:emit_signal("property::"..prop, self._private[prop])
end
imagebox["get_"..prop] = function(self)
if self._private[prop] == nil then
return defaults[prop]
end
return self._private[prop]
end
end
--- Returns a new `wibox.widget.imagebox` instance.
--
-- This is the constructor of `wibox.widget.imagebox`. It creates a new
@ -692,14 +272,12 @@ local function new(image, resize_allowed, clip_shape, ...)
local ret = base.make_widget(nil, nil, {enable_properties = true})
gtable.crush(ret, imagebox, true)
ret._private.resize = true
if image then
ret:set_image(image)
end
if resize_allowed ~= nil then
ret.resize = resize_allowed
ret:set_resize(resize_allowed)
end
ret._private.clip_shape = clip_shape
@ -712,6 +290,10 @@ function imagebox.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(imagebox, imagebox.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -8,7 +8,6 @@
-- @author Emmanuel Lepage Valle
-- @copyright 2012 Emmanuel Lepage Vallee
-- @widgetmod wibox.widget.piechart
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local color = require( "gears.color" )
@ -261,5 +260,9 @@ local function new(data_list)
return ret
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(module, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -18,7 +18,6 @@
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @widgetmod wibox.widget.progressbar
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -521,6 +520,10 @@ function progressbar.mt:__call(...)
return progressbar.new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(progressbar, progressbar.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -20,7 +20,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2014, 2017 Emmanuel Lepage Vallee
-- @widgetmod wibox.widget.separator
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local beautiful = require( "beautiful" )
local base = require( "wibox.widget.base" )
@ -216,5 +215,9 @@ local function new(args)
return ret
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(separator, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -7,7 +7,6 @@
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2015 Grigory Mishchenko, 2016 Emmanuel Lepage Vallee
-- @widgetmod wibox.widget.slider
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -325,7 +324,7 @@ function slider:draw(_, cr, width, height)
local handle_height, handle_width = height, self._private.handle_width
or beautiful.slider_handle_width
or math.floor(height/2)
or height/2
local handle_border_width = self._private.handle_border_width
or beautiful.slider_handle_border_width
@ -363,7 +362,7 @@ function slider:draw(_, cr, width, height)
end
else
bar_height = bar_height or beautiful.slider_bar_height or height
y_offset = math.floor((height - bar_height)/2)
y_offset = (height - bar_height)/2
end
@ -380,10 +379,8 @@ function slider:draw(_, cr, width, height)
bar_shape(cr, width - x_offset - right_margin, bar_height or height)
if bar_active_color and type(bar_color) == "string" and type(bar_active_color) == "string" then
local bar_active_width = math.floor(
active_rate * (width - x_offset - right_margin)
local bar_active_width = active_rate * (width - x_offset - right_margin)
- (handle_width - handle_border_width/2) * (active_rate - 0.5)
)
cr:set_source(color.create_pattern{
type = "linear",
from = {0,0},
@ -455,7 +452,7 @@ function slider:draw(_, cr, width, height)
-- Get the widget size back to it's non-transfored value
local min, _, interval = get_extremums(self)
local rel_value = math.floor(((value-min)/interval) * (width-handle_width))
local rel_value = ((value-min)/interval) * (width-handle_width)
cr:translate(x_offset + rel_value, y_offset)
@ -541,6 +538,10 @@ function slider.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(slider, slider.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -2,7 +2,6 @@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @widgetmod wibox.widget.systray
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local wbase = require("wibox.widget.base")
@ -225,6 +224,10 @@ function systray.mt:__call(...)
return instance
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(systray, systray.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,12 +1,10 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_widget_defaults_textbox_EXAMPLE@
--
-- @author Uli Schlachter
-- @author dodo
-- @copyright 2010, 2011 Uli Schlachter, dodo
-- @widgetmod wibox.widget.textbox
-- @supermodule wibox.widget.base
---------------------------------------------------------------------------
local base = require("wibox.widget.base")
@ -148,7 +146,7 @@ function textbox:get_height_for_width_at_dpi(width, dpi)
end
--- Set the text of the textbox.(with
-- [Pango markup](https://docs.gtk.org/Pango/pango_markup.html)).
-- [Pango markup](https://developer.gnome.org/pango/stable/pango-Markup.html)).
--
-- @tparam string text The text to set. This can contain pango markup (e.g.
-- `<b>bold</b>`). You can use `gears.string.escape` to escape
@ -178,7 +176,7 @@ function textbox:set_markup_silently(text)
end
--- Set the text of the textbox (with
-- [Pango markup](https://docs.gtk.org/Pango/pango_markup.html)).
-- [Pango markup](https://developer.gnome.org/pango/stable/pango-Markup.html)).
--
-- @property markup
-- @tparam string text The text to set. This can contain pango markup (e.g.
@ -222,22 +220,17 @@ function textbox:get_text()
return self._private.layout.text
end
--- Set the text ellipsize mode.
--- Set a textbox ellipsize mode.
--
-- Valid values are:
--
-- * `"start"`
-- * `"middle"`
-- * `"end"`
-- * `"none"`
--
-- See Pango for additional details:
-- [Layout.set_ellipsize](https://docs.gtk.org/Pango/method.Layout.set_ellipsize.html)
--
--@DOC_wibox_widget_textbox_ellipsize_EXAMPLE@
-- * **start**
-- * **middle**
-- * **end**
--
-- @property ellipsize
-- @tparam string mode The ellipsize mode.
-- @tparam string mode Where should long lines be shortened? "start", "middle"
-- or "end".
-- @propemits true false
function textbox:set_ellipsize(mode)
@ -380,26 +373,9 @@ function textbox.mt.__call(_, ...)
return new(...)
end
--- Get geometry of text label, as if textbox would be created for it on the screen.
--
-- @tparam string text The text content, pango markup supported.
-- @tparam[opt=nil] integer|screen s The screen on which the textbox would be displayed.
-- @tparam[opt=beautiful.font] string font The font description as string.
-- @treturn table Geometry (width, height) hashtable.
-- @staticfct wibox.widget.textbox.get_markup_geometry
function textbox.get_markup_geometry(text, s, font)
font = font or beautiful.font
local pctx = PangoCairo.font_map_get_default():create_context()
local playout = Pango.Layout.new(pctx)
playout:set_font_description(beautiful.get_font(font))
local dpi_scale = beautiful.xresources.get_dpi(s)
pctx:set_resolution(dpi_scale)
playout:context_changed()
local attr, parsed = Pango.parse_markup(text, -1, 0)
playout.attributes, playout.text = attr, parsed
local _, logical = playout:get_pixel_extents()
return logical
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(textbox, textbox.mt)

View File

@ -1,46 +1,9 @@
---------------------------------------------------------------------------
--- Text clock widget.
--
-- The `wibox.widget.textclock` widget is part of the Awesome WM's widget
-- system (see @{03-declarative-layout.md}).
--
-- This widget displays a text clock formatted by the
-- [GLib Date Time format](https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format)
-- and [GTimeZone](https://developer.gnome.org/glib/stable/glib-GTimeZone.html#g-time-zone-new).
--
-- The `wibox.widget.textclock` inherits from `wibox.widget.textbox`. It means
-- that, once created, the user will receive a derivated instance of
-- `wibox.widget.textbox` associated with a private `gears.timer` to manage
-- timed updates of the displayed clock.
--
-- Use a `wibox.widget.textclock`
-- ---
--
-- @DOC_wibox_widget_defaults_textclock_EXAMPLE@
--
-- Alternatively, you can declare the `textclock` widget using the
-- declarative pattern (Both codes are strictly equivalent):
--
-- @DOC_wibox_widget_declarative-pattern_textclock_EXAMPLE@
--
-- The GLib DateTime format
-- ---
--
-- The time displayed by the textclock widget can be formated by the GLib
-- DateTime format.
--
-- Here is a short list with commonly used format specifiers (extracted from
-- the Glib API references):
--
--@DOC_glib_timedate_format_COMMON@
--
-- You can read more on the GLib DateTime format in the
-- [GLib documentation](https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format).
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @widgetmod wibox.widget.textclock
-- @supermodule wibox.widget.textbox
---------------------------------------------------------------------------
local setmetatable = setmetatable
@ -163,6 +126,10 @@ function textclock.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(textclock, textclock.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

4
luaa.h
View File

@ -31,10 +31,6 @@
#include "common/lualib.h"
#include "common/luaclass.h"
#if !(501 <= LUA_VERSION_NUM && LUA_VERSION_NUM < 504)
#error "Awesome only supports Lua versions 5.1-5.3 and LuaJIT2, please refer to https://awesomewm.org/apidoc/documentation/10-building-and-testing.md.html#Building"
#endif
#define luaA_deprecate(L, repl) \
do { \
luaA_warn(L, "%s: This function is deprecated and will be removed, see %s", \

View File

@ -82,7 +82,7 @@ mousegrabber_handleevent(lua_State *L, int x, int y, uint16_t mask)
* The function is called with one argument:
* a table containing modifiers pointer coordinates.
*
* The list of valid cursors is:
* The list of valid cusrors are:
*
*@DOC_cursor_c_COMMON@
*

Some files were not shown because too many files have changed in this diff Show More