Commit f648f0af authored by Alexander Mikhaylenko's avatar Alexander Mikhaylenko

Initial commit

parents
#!/usr/bin/env python3
from os import environ, path
from subprocess import call
prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local')
datadir = path.join(prefix, 'share')
destdir = environ.get('DESTDIR', '')
# Package managers set this so we don't need to run
if not destdir:
print('Updating icon cache...')
call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')])
print('Updating desktop database...')
call(['update-desktop-database', '-q', path.join(datadir, 'applications')])
print('Compiling GSettings schemas...')
call(['glib-compile-schemas', path.join(datadir, 'glib-2.0', 'schemas')])
desktop_file = i18n.merge_file(
input: 'org.gnome.Mockup.desktop.in',
output: 'org.gnome.Mockup.desktop',
type: 'desktop',
po_dir: '../po',
install: true,
install_dir: join_paths(get_option('datadir'), 'applications')
)
desktop_utils = find_program('desktop-file-validate', required: false)
if desktop_utils.found()
test('Validate desktop file', desktop_utils,
args: [desktop_file]
)
endif
appstream_file = i18n.merge_file(
input: 'org.gnome.Mockup.appdata.xml.in',
output: 'org.gnome.Mockup.appdata.xml',
po_dir: '../po',
install: true,
install_dir: join_paths(get_option('datadir'), 'appdata')
)
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
test('Validate appstream file', appstream_util,
args: ['validate', appstream_file]
)
endif
install_data('org.gnome.Mockup.gschema.xml',
install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas')
)
compile_schemas = find_program('glib-compile-schemas', required: false)
if compile_schemas.found()
test('Validate schema file', compile_schemas,
args: ['--strict', '--dry-run', meson.current_source_dir()]
)
endif
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>org.gnome.Mockup.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>LicenseRef-proprietary</project_license>
<description>
</description>
</component>
[Desktop Entry]
Name=mockup
Exec=mockup
Terminal=false
Type=Application
Categories=GTK;
StartupNotify=true
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="mockup">
<schema id="org.gnome.Mockup" path="/org/gnome/Mockup/">
</schema>
</schemalist>
project('mockup', ['c', 'vala'], version: '0.1.0',
meson_version: '>= 0.40.0',
)
i18n = import('i18n')
subdir('data')
subdir('src')
subdir('po')
meson.add_install_script('build-aux/meson/postinstall.py')
\ No newline at end of file
{
"app-id": "org.gnome.Mockup",
"runtime": "org.gnome.Platform",
"runtime-version": "master",
"sdk": "org.gnome.Sdk",
"command": "mockup",
"finish-args": [
"--share=network",
"--share=ipc",
"--socket=x11",
"--socket=wayland",
"--filesystem=xdg-run/dconf",
"--filesystem=~/.config/dconf:ro",
"--talk-name=ca.desrt.dconf",
"--env=DCONF_USER_CONFIG_DIR=.config/dconf"
],
"build-options": {
"cflags": "-O2 -g",
"cxxflags": "-O2 -g",
"env": {
"V": "1"
}
},
"cleanup": [
"/include",
"/lib/pkgconfig",
"/man",
"/share/doc",
"/share/gtk-doc",
"/share/man",
"/share/pkgconfig",
"/share/vala",
"*.la",
"*.a"
],
"modules": [
{
"name": "mockup",
"buildsystem": "meson",
"config-opts": [ "--libdir=lib" ],
"builddir": true,
"sources": [
{
"type": "git",
"url": "file:///home/exalm/Projects/Mockup"
}
]
}
]
}
data/org.gnome.Mockup.desktop.in
data/org.gnome.Mockup.appdata.xml.in
data/org.gnome.Mockup.gschema.xml
src/window.ui
src/main.vala
src/window.vala
i18n.gettext('mockup', preset: 'glib')
This diff is collapsed.
This diff is collapsed.
int main (string[] args) {
var app = new Gtk.Application ("org.gnome.Mockup", ApplicationFlags.FLAGS_NONE);
app.activate.connect (() => {
var win = app.active_window;
if (win == null) {
win = new Mockup.Window (app);
}
win.present ();
});
return app.run (args);
}
mockup_sources = [
'main.vala',
'phone-screen.vala',
'swipe-tracker.vala',
'window.vala',
]
mockup_deps = [
dependency('gio-2.0', version: '>= 2.50'),
dependency('gtk+-3.0', version: '>= 3.24'),
dependency('librsvg-2.0'),
]
gnome = import('gnome')
mockup_sources += gnome.compile_resources('mockup-resources',
'mockup.gresource.xml',
c_name: 'mockup'
)
executable('mockup', mockup_sources,
vala_args: '--target-glib=2.50', dependencies: mockup_deps,
install: true,
)
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/Mockup">
<file>window.ui</file>
<file>apps.svg</file>
<file>appdrawer.svg</file>
<file>topbar.svg</file>
<file>wallpaper.png</file>
</gresource>
</gresources>
\ No newline at end of file
public class Mockup.PhoneScreen : Gtk.DrawingArea {
//360 654
private double _progress;
public double progress {
get { return _progress; }
set {
_progress = value;
queue_draw ();
}
}
private Cairo.Surface topbar;
private Cairo.Surface appdrawer;
private Cairo.Surface apps;
private Cairo.Surface wallpaper;
private double topbar_height;
private double bottombar_height;
private double appdrawer_partial_height;
private Rsvg.Rectangle window_rect;
construct {
var topbar_handle = load_svg ("topbar");
var apps_handle = load_svg ("apps");
var appdrawer_handle = load_svg ("appdrawer");
topbar_height = get_rect (topbar_handle, null).height;
bottombar_height = 40;
appdrawer_partial_height = 183 - bottombar_height;
window_rect = get_rect (apps_handle, "#window");
topbar = cache_svg (topbar_handle);
appdrawer = cache_svg (appdrawer_handle);
apps = cache_svg (apps_handle);
wallpaper = load_png ("wallpaper");
set_size_request ((int) Math.round (window_rect.width), (int) Math.round (topbar_height + window_rect.height + bottombar_height));
}
private Cairo.Surface load_png (string name) {
try {
var pixbuf = new Gdk.Pixbuf.from_resource ("/org/gnome/Mockup/%s.png".printf (name));
return Gdk.cairo_surface_create_from_pixbuf (pixbuf, 1, get_window ());
}
catch (Error e) {
critical ("Cannot load png: %s", name);
return new Cairo.ImageSurface (Cairo.Format.ARGB32, 256, 256);
}
}
private Cairo.Surface cache_svg (Rsvg.Handle handle) {
var rect = get_rect (handle, null);
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, (int) rect.width, (int) rect.height);
var context = new Cairo.Context (surface);
handle.render_cairo (context);
return surface;
}
private Rsvg.Handle load_svg (string name) {
try {
var bytes = resources_lookup_data ("/org/gnome/Mockup/%s.svg".printf (name), ResourceLookupFlags.NONE);
var data = bytes.get_data ();
return new Rsvg.Handle.from_data (data);
}
catch (Error e) {
critical ("Cannot load svg: %s", name);
return new Rsvg.Handle ();
}
}
private Rsvg.Rectangle get_rect (Rsvg.Handle handle, string? sub) {
Rsvg.Rectangle rect = {};
if (!handle.get_geometry_sub (out rect, null, sub))
assert_not_reached ();
return rect;
}
public override bool draw (Cairo.Context cr) {
cr.rectangle (0, 0, get_allocated_width (), topbar_height + bottombar_height + window_rect.height);
cr.clip ();
// cr.set_source_rgb(1, 0, 0);
cr.set_source_surface (wallpaper, 0, 0);
cr.paint ();
cr.save ();
cr.translate (0, topbar_height);
double window_offset = 1 - double.min (progress, 1);
double scale = 0.6 + 0.4 * window_offset;
cr.translate (window_rect.width / 2, window_rect.height / 2 - appdrawer_partial_height - bottombar_height);
cr.scale (scale, scale);
cr.translate (-window_rect.x - window_rect.width / 2, -window_rect.y - window_rect.height / 2 + appdrawer_partial_height + bottombar_height);
cr.set_source_surface (apps, 0, 0);
cr.paint ();
cr.restore();
cr.save ();
cr.translate (0, topbar_height + window_rect.height);
double offset = 0;
if (progress <= 1)
offset = -appdrawer_partial_height * progress;
else
offset = -appdrawer_partial_height + (window_rect.height - appdrawer_partial_height) * (1 - progress);
cr.translate (0, offset);
cr.set_source_surface (appdrawer, 0, 0);
cr.paint ();
cr.restore();
cr.set_source_surface (topbar, 0, 0);
cr.paint ();
return false;
}
}
double easeOutCubic (double t) {
double p = t - 1;
return p * p * p + 1;
}
public class Mockup.SwipeTracker : Object {
private const double SWIPE_SPEED = 400;
private const double SCROLL_MULTIPLIER = 10;
private const int64 MIN_ANIMATION_DURATION = 100;
private const int64 MAX_ANIMATION_DURATION = 400;
private const double CANCEL_AREA = 0.5;
private const double VELOCITY_THRESHOLD = 0.001;
private const double DURATION_MULTIPLIER = 3;
public enum Direction {
FORWARD = -1,
BACK = 1,
}
private enum State {
NONE,
PENDING,
SCROLLING,
DECELERATING,
}
public signal void begin (Direction direction);
public signal void update (double progress);
public signal void end (bool cancel);
public Gtk.Widget widget { get; construct; }
public Gtk.Orientation orientation { get; construct; }
private Gtk.EventControllerScroll scroll_controller;
private Gtk.GestureDrag touch_gesture;
private Gtk.GestureDrag three_touch_gesture;
private State state;
private uint32 prev_time;
private double velocity;
private uint tick_cb_id;
private int64 start_time;
private int64 end_time;
private double progress;
private double start_progress;
private double end_progress;
private bool cancelled;
private double prev_offset;
public bool can_swipe_back { get; set; }
public bool can_swipe_forward { get; set; }
public SwipeTracker (Gtk.Widget widget, Gtk.Orientation orientation) {
Object (widget: widget, orientation: orientation);
}
construct {
reset ();
scroll_controller = new Gtk.EventControllerScroll (widget, Gtk.EventControllerScrollFlags.BOTH_AXES);
scroll_controller.propagation_phase = Gtk.PropagationPhase.CAPTURE;
touch_gesture = Object.new (typeof (Gtk.GestureDrag),
"widget", widget,
"n-points", 1,
null) as Gtk.GestureDrag;
touch_gesture.propagation_phase = Gtk.PropagationPhase.CAPTURE;
three_touch_gesture = Object.new (typeof (Gtk.GestureDrag),
"widget", widget,
"n-points", 3,
null) as Gtk.GestureDrag;
touch_gesture.propagation_phase = Gtk.PropagationPhase.CAPTURE;
scroll_controller.scroll_begin.connect (() => gesture_begin ());
scroll_controller.scroll.connect ((dx, dy) => {
gesture_update ((is_vertical () ? -dy : -dx) * SCROLL_MULTIPLIER / SWIPE_SPEED);
});
scroll_controller.scroll_end.connect (() => gesture_end ());
three_touch_gesture.drag_begin.connect (() => gesture_begin ());
three_touch_gesture.drag_update.connect ((offset_x, offset_y) => {
double offset = 0;
if (is_vertical ())
offset = offset_y / SWIPE_SPEED;
else
offset = offset_x / SWIPE_SPEED;
gesture_update (offset - prev_offset);
prev_offset = offset;
});
three_touch_gesture.drag_end.connect ((offset_x, offset_y) => gesture_end ());
touch_gesture.drag_begin.connect (() => gesture_begin ());
touch_gesture.drag_update.connect ((offset_x, offset_y) => {
double offset = 0;
if (is_vertical ())
offset = offset_y / (double) widget.get_allocated_height ();
else
offset = offset_x / (double) widget.get_allocated_width ();
gesture_update (offset - prev_offset);
prev_offset = offset;
});
touch_gesture.drag_end.connect ((offset_x, offset_y) => gesture_end ());
}
public void set_initial_progress (double progress) {
this.progress = progress;
}
private void reset () {
state = State.NONE;
if (tick_cb_id != 0) {
widget.remove_tick_callback (tick_cb_id);
tick_cb_id = 0;
}
prev_offset = 0;
progress = 0;
start_progress = 0;
end_progress = 0;
start_time = 0;
end_time = 0;
prev_time = 0;
velocity = 0;
cancelled = false;
}
private bool is_vertical () {
return (orientation == Gtk.Orientation.VERTICAL);
}
private void gesture_begin () {
if (state == State.SCROLLING || state == State.PENDING)
return;
prev_time = Gtk.get_current_event ().get_time ();
if (state == State.DECELERATING) {
widget.remove_tick_callback (tick_cb_id);
tick_cb_id = 0;
prev_offset = 0;
state = State.SCROLLING;
} else
state = State.PENDING;
}
private void gesture_update (double delta) {
if (state != State.PENDING && state != State.SCROLLING)
return;
var overshoot = (progress + delta > 0 && !can_swipe_back) || (progress + delta < 0 && !can_swipe_forward);
if (progress * (progress + delta) <= 0) {
if (state == State.SCROLLING) {
update (0);
end (true);
}
state = State.PENDING;
if (!overshoot) {
begin (progress + delta > 0 ? Direction.BACK : Direction.FORWARD);
update (0);
state = State.SCROLLING;
}
}
var time = Gtk.get_current_event ().get_time ();
if (overshoot) {
progress = 0;
velocity = 0;
} else {
progress += delta;
if (time != prev_time)
velocity = delta / (time - prev_time);
var max_progress = (progress > 0) ? 1 : 0;
var min_progress = (progress < 0) ? -1 : 0;
progress = progress.clamp (min_progress, max_progress);
update (progress);
}
prev_time = time;
}
private bool should_cancel () {
if (progress == 0)
return true;
if (progress > 0 && !can_swipe_back)
return true;
if (progress < 0 && !can_swipe_forward)
return true;
if (velocity * progress < 0)
return true;
return progress.abs () < CANCEL_AREA && velocity.abs () < VELOCITY_THRESHOLD;
}
private void gesture_end () {
if (state == State.PENDING) {
reset ();
return;
}
if (state != State.SCROLLING)
return;
cancelled = should_cancel ();
start_progress = progress;
if (cancelled)
end_progress = 0;
else
end_progress = (progress > 0) ? 1 : -1;
var duration = MAX_ANIMATION_DURATION;
if ((end_progress - progress) * velocity > 0) {
duration = (int64) ((progress - end_progress) / velocity * DURATION_MULTIPLIER).abs ();
duration = duration.clamp (MIN_ANIMATION_DURATION, MAX_ANIMATION_DURATION);
}
start_animation (duration);
}
private void start_animation (int64 duration) {
state = State.DECELERATING;
if (duration <= 0 || (velocity == 0 && progress == end_progress)) {
end_animation ();
return;
}
start_time = widget.get_frame_clock ().get_frame_time () / 1000;
end_time = start_time + duration;
tick_cb_id = widget.add_tick_callback (tick_cb);
}
private bool tick_cb (Gtk.Widget widget, Gdk.FrameClock frame_clock) {
assert (state == State.DECELERATING);
assert (end_time > start_time);
int64 frame_time = frame_clock.get_frame_time () / 1000;
double animation_progress = (double) (frame_time - start_time) / (double) (end_time - start_time);
if (animation_progress > 1)
animation_progress = 1;
progress = start_progress + (end_progress - start_progress) * easeOutCubic (animation_progress);
update (progress);
if (frame_time >= end_time) {
tick_cb_id = 0;
end_animation ();
return Source.REMOVE;
}
return Source.CONTINUE;
}
private void end_animation () {
progress = end_progress;
end (cancelled);
reset ();
}
private void animate (Direction direction, double p, int64 duration) {
if (state != State.NONE)
return;
begin (direction);
update (0);
cancelled = false;
start_progress = progress;
end_progress = p;
start_animation (duration);
}
public void animate_back (int64 duration) {
if (!can_swipe_back)
return;
animate (Direction.BACK, 1, duration);
}
public void animate_forward (int64 duration) {
if (!can_swipe_forward)
return;
animate (Direction.FORWARD, -1, duration);
}
}
\ No newline at end of file
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="MockupWindow" parent="GtkApplicationWindow">
<property name="default-width">100</property>
<property name="default-height">100</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>
<property name="show-close-button">True</property>
<property name="title">Mockup</property>
</object>
</child>
<child>
<object class="MockupPhoneScreen" id="phone_screen">
<property name="visible">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
</child>
</template>
</interface>
[GtkTemplate (ui = "/org/gnome/Mockup/window.ui")]
public class Mockup.Window : Gtk.ApplicationWindow {
[GtkChild]
private PhoneScreen phone_screen;
private SwipeTracker tracker;
private SwipeTracker.Direction? direction;
private int state;
public Window (Gtk.Application app) {
Object (application: app);
Gtk.Settings.get_default ().gtk_application_prefer_dark_theme = true;
phone_screen.add_events (Gdk.EventMask.ALL_EVENTS_MASK);
state = 0;
tracker = new SwipeTracker (phone_screen, Gtk.Orientation.VERTICAL);
tracker.can_swipe_forward = true;
tracker.begin.connect (dir => {
direction = dir;
});
tracker.update.connect (progress => {
phone_screen.progress = state - progress;
});
tracker.end.connect (cancel => {
if (!cancel)
state += (direction == SwipeTracker.Direction.BACK ? -1 : 1);
tracker.can_swipe_back = state > 0;
tracker.can_swipe_forward = state < 2;
});
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment