An Edje Knob. Here is a commented and abbreviated .edc file for a knob-like behaviour of an elementary slider widget.
It is inspired by Raoul Hecky's elm_slider_knob. It will look like this: and works by displaying one of 60 images, depending on how much you drag the knob (up and down), and each of the images shows a different rotation of the knob. The images are here as zip and tar.
Actually there is a knobbg.png, which is the background of the knob that doesn't change, and there are 0000.png to 0060.png which only show the disc of the knob that changes when the knob rotates.
The .edc and .edj files, a demo C program and the images are here: edje-knob.tar or here:
When you unpack one of these archives and change to the directory you can compile the .edc file and test the result with edje_player:
edje_cc -id images/ knob.edc knob.edj
edje_player knob.edj
You need to install EFL to do this. As a quick overview, theming of Elementary widgets can be done like this:
- Write an .edc file and create an .edj from it
- Use the edje file in your program roughly like this:

elm_theme_extension_add(NULL, "./knob.edj");    // load the .edj file
Evas_Object *knob_slider = elm_slider_add(win); // add a slider widget
elm_object_style_set(knob_slider, "knob");      // set the theme
                                   ^^^^~~~~~~~~ This is the name of the group (see below)
A demo program will be described here, but first we go through the knob.edc.
Our .edc file contains a

group block,

which here contains a




programs block.

------------------------------------------------- knob.edc -----------------------


This is the name we will use in the C source to select this style in elm_object_style_set.

name: "elm/slider/horizontal/knob"; alias: "elm/slider/horizontal/default"; min: 64 64;

Here we list the images we are using to display the knob. They will be supplied to edje_cc with -id PATH_TO_IMAGES.

images{ image: "0000.png" COMP; image: "0001.png" COMP; image: "0002.png" COMP

Similar for 0003.png to 0059.png.

image: "0060.png" COMP; image: "knobbg.png" COMP; } script{

The script block contains global variables and functions. update_knob_state is used to load new images when the knob is turned.

public knob_pos; public knob_ref; public knob_last; public knob_move; public signal_from_dragger; public update_knob_state(Float:frac){ new px, py, pw, ph; get_geometry(PART:"knob", px, py, pw, ph); new Float:step = ph/60; if(frac > ph) { set_state(PART:"knob", "60", 0.0); return;} if(frac <= 0) { set_state(PART:"knob", "default", 0.0); return;} if(frac < step) { set_state(PART:"knob", "1", 0.0); return;} if(frac < 2*step) { set_state(PART:"knob", "2", 0.0); return;}

Similar "if" statments for frac < 3*step to 59*step.

if(frac < 60*step) { set_state(PART:"knob", "60", 0.0); return;} } public reset_dragger(){ set_drag(PART:"dragger", 0.0, 0.0); set_int(signal_from_dragger, 0); } } parts{

The parts block contains the functional parts of the knob.
These are mostly geometric objects that may be moveable and/or visible.
They may also emit signals which will be important in the programs block below.

part{ name: "knobbase"; scale: 1; description{ state: "default" 0.0; min: 64 64; image.normal: "knobbg.png"; } } part{ name: "knob"; mouse_events: 0; scale: 1;

Each part has a one or more descriptions, which represent states of the part.
Here each state is associated with one image (position) of the knob.

description{ state: "default" 0.0; min: 64 64; "knobbase"; image.normal: "0000.png"; } description{ state: "1" 0.0; inherit: "default" 0.0; image.normal: "0001.png"; } description{ state: "2" 0.0; inherit: "default" 0.0; image.normal: "0002.png"; }

Descriptions for states "3" to "59" similar.

description { state: "60" 0.0; inherit: "default" 0.0; image.normal: "0060.png"; } } part{

This part is needed, so we can set the value of the knob from the C source via elm_slider_value_set.

//The real slider for elm_slider name: "elm.dragable.slider"; scale: 1; dragable.x: 1 1 0; dragable.y: 1 1 0; type: RECT; description{ state: "default" 0.0; visible: 0; } dragable{ x: 1 1 0; y: 1 1 0; } } part{

This part is the actually draggable part of the slider.
It's invisible but will measure, how much the knob has been turned (or dragged).

name: "dragger"; type: RECT; description{ state: "default" 0.0; "knob"; "knob"; color: 0 0 0 0; } description{ state: "hoki" 0.0; "knob"; "knob"; color: 0 0 0 0; } dragable{ x: 0 0 0; y: 1 1 0; } } } programs{

The program block contains small scripts which define
the behaviour of our knob.
The programs are called when a certain signal
from a certain source is received. Signals can be emitted from inside this edje
or from outside (C source).


The script part of this program runs when
signal "drag" from source "dragger" (the part defined above) is received.

name: "on_drag_move"; signal: "drag"; source: "dragger"; script{ new Float:p1; new Float:p2;

The drag value is subtracted from the last knob position, because a drag upwards yields negative values.

get_drag(PART:"dragger", p1, p2); set_float(knob_pos, get_float(knob_last) - p2); new px, py, pw, ph; get_geometry(PART:"knob", px, py, pw, ph); if(get_float(knob_pos) > ph) set_float(knob_pos, ph); if(get_float(knob_pos) < 0) set_float(knob_pos, 0); update_knob_state(get_float(knob_pos)); new Float:sl_val = get_float(knob_pos)/ph;

The variable "signal_from_dragger" is used as a guard to distinguish if calls of set_drag for "elm.dragable.slider"
are made from here (then it is 1), ore from outside, e.g. C source, (then it is 0).

set_int(signal_from_dragger, 1); set_drag(PART:"elm.dragable.slider", sl_val , sl_val); } } program{ name: "on_drag_stop"; signal: "drag,stop"; source: "dragger"; script{ set_float(knob_last, get_float(knob_pos)); reset_dragger(); } } program{ name: "on_wheel"; signal: "mouse,wheel*"; source: "dragger"; //after: "on_slider_set"; } program{ signal: "elm,state,enabled"; source: "elm"; script { set_int(signal_from_dragger, 0); } } program{

This program is called when set_drag for elm.dragable.slider is called.

signal: "drag,set"; source: "elm.dragable.slider"; script {

If we were called because the part "dragger" was dragged, "signal_from_dragger" is 1 and nothing else
will happen here except that "signal_from_dragger" will be set to 0.

if(get_int(signal_from_dragger) == 0){ new Float:p1; new Float:p2; get_drag(PART:"elm.dragable.slider", p1, p2); new px, py, pw, ph; get_geometry(PART:"knob", px, py, pw, ph); set_float(knob_pos, ph*p1); if(get_float(knob_pos) > ph) set_float(knob_pos, ph); if(get_float(knob_pos) < 0) set_float(knob_pos, 0); set_float(knob_last, get_float(knob_pos)); update_knob_state(get_float(knob_pos)); new Float:sl_val = get_float(knob_pos)/ph; set_drag(PART:"elm.dragable.slider", sl_val , sl_val); }else{ set_int(signal_from_dragger, 0); } } } } } }