from gi.repository import Gtk, WebKit, GObject

HTML = """<html>
<head>
<title>This is a title</title>
<style>
body {
    background-image: repeating-linear-gradient(180deg, #eee, #eee 20px, #ddd 20px, #ddd 40px);
}
#embed {
    width: 100%;
    height: 200px;
    background: white;
    border: 1px solid black;
}
</style>
</head>

<body>
<h1>Here it is...</h1>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<div id="embed"></div>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
<p>Other text</p>
</body>
</html>"""

INNER_HTML = """<html>
<head>
<style>
body {
    background-image: repeating-linear-gradient(180deg, #ffd, #ffd 20px, #eef 20px, #eef 40px);
}
</style>
</head>

<body>
<p>Please edit me!</p>
</body>
</html>"""

MIN_HEIGHT = 200

class Overlay(Gtk.Overlay, Gtk.Scrollable):
    
    __gproperties__ = {
        'hadjustment': (Gtk.Adjustment, 'hadjustment', 'hadjustment', GObject.PARAM_READWRITE),
        'vadjustment': (Gtk.Adjustment, 'vadjustment', 'vadjustment', GObject.PARAM_READWRITE),
        'hscroll-policy': (Gtk.ScrollablePolicy, 'hscroll', 'hscroll', Gtk.ScrollablePolicy.MINIMUM,
                           GObject.PARAM_READWRITE),
        'vscroll-policy': (Gtk.ScrollablePolicy, 'vscroll', 'vscroll', Gtk.ScrollablePolicy.MINIMUM,
                           GObject.PARAM_READWRITE)
    }
    
    def __init__(self, main_child):
        Gtk.Overlay.__init__(self)
        
        self.add(main_child)
        self.main_child = main_child
    
    def do_get_property(self, property):
        #print "Get", property.name
        method = getattr(self.main_child, 'get_' + property.name.replace('-', '_'))
        return method()
    
    def do_set_property(self, property, value):
        #print "Set", property.name
        method = getattr(self.main_child, 'set_' + property.name.replace('-', '_'))
        method(value)
    
    def on_scroll(self, *args):
        #print "Scroll"
        self.queue_resize()
    
GObject.type_register(Overlay)

class InnerWebView(Gtk.Box):
    
    def __init__(self, parent):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
        self.view = WebKit.WebView()
        self.sw = Gtk.ScrolledWindow()
        self.parent = parent
        self.last_height = -1
        self.last_scroll = 0
        
        self.sw.add(self.view)
        self.pack_start(Gtk.Label(label="This is the top"), False, False, 0)
        self.pack_start(self.sw, True, True, 0)
        self.pack_start(Gtk.Label(label="This is the bottom"), False, False, 0)
        
        self.view.load_string(INNER_HTML, "text/html", "UTF-8", "file://")
        self.view.set_editable(True)
        
        self.connect('realize', self.on_realize)
    
    def on_size_changed(self, adj):
        view_height = adj.get_upper()
        print view_height
        if view_height > 0 and view_height != self.last_height:
            widget_height = view_height + self.get_allocated_height() - self.view.get_allocated_height() - 2
            self.parent.notify_inner_height(widget_height)
            self.last_height = view_height
    
    def on_realize(self, *args):
        scrollbar = self.sw.get_vscrollbar();
        scrollbar.hide()
        
        adj = self.view.get_vadjustment()
        adj.connect('value-changed', self.on_scrolled)
        adj.connect('changed', self.on_size_changed)
    
    def on_scrolled(self, adj):
        delta = adj.get_value() - self.last_scroll
        self.last_scroll = adj.get_value()
        if delta:
            self.parent.inner_scroll(delta)

class WebKitWindow(Gtk.Window):
    
    def __init__(self, filename=""):
        Gtk.Window.__init__(self)
        self.setting_scroll = False
        
        self.set_title("Nested WebViews")
        self.connect('destroy', self.on_quit)
        self.set_default_size(300,400)
        
        sw = Gtk.ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.add(sw)
        
        self.view = WebKit.WebView()
        self.overlay = Overlay(self.view)
        sw.add(self.overlay)
        
        self.inner = InnerWebView(self)
        self.inner.set_valign(Gtk.Align.FILL)
        self.inner.set_halign(Gtk.Align.FILL)
        self.overlay.add_overlay(self.inner)
        self.overlay.connect('get-child-position', self.on_child_position)
        
        self.show_all()
        self.view.load_string(HTML, "text/html", "UTF-8", "file://")
        
        self.view.get_hadjustment().connect('value-changed', self.overlay.on_scroll)
        self.view.get_vadjustment().connect('value-changed', self.overlay.on_scroll)
        
        Gtk.main()
    
    def on_child_position(self, overlay, widget, allocation):
        if widget != self.inner:
            return False
        
        doc = self.view.get_dom_document()
        div = doc.query_selector('#embed')
        if div:
            view_height = overlay.get_allocated_height()
            div_height = div.get_offset_height()
            y_top = div.get_offset_top() - self.overlay.get_vadjustment().get_value()
            available_height = min(y_top + div_height, view_height) - max(y_top, 0)
            
            if available_height < 0 or available_height == div_height:
                allocation.height = div_height
                allocation.y = y_top
            elif available_height > MIN_HEIGHT:
                allocation.y = max(y_top, 0)
                allocation.height = available_height
            else:
                allocation.y = max(y_top, min(y_top + div_height - MIN_HEIGHT, 0))
                allocation.height = MIN_HEIGHT
            # Offset includes borders
            allocation.width = div.get_offset_width()
            allocation.x = div.get_offset_left() - self.overlay.get_hadjustment().get_value()
            
            offset = allocation.y - y_top
            self.setting_scroll = True
            self.inner.view.get_vadjustment().set_value(offset)
            self.setting_scroll = False
            #print offset
            return True
        else:
            return False
    
    def notify_inner_height(self, height):
        div = self.view.get_dom_document().query_selector('#embed');
        if div:
            div.get_style().set_property('height', str(max(height, MIN_HEIGHT)), '')
    
    def inner_scroll(self, delta):
        if not self.setting_scroll:
            adj = self.overlay.get_vadjustment()
            adj.set_value(adj.get_value() + delta)
    
    def on_quit(self, *args):
        Gtk.main_quit()


if __name__ == '__main__':
    import sys
    WebKitWindow(*sys.argv[1:])
